Skip to content

403 Forbidden Error for URLs with Encoded Question Marks (Apache)

Problem Statement

After recent Apache Web Server updates, many applications started returning 403 Forbidden errors when URLs contained an encoded question mark (%3F) in GET parameters. This commonly occurs when using PHP's rawurlencode() on user input that contains question marks, as demonstrated in this code snippet:

php
<!-- Returns 403 if $test contains "?" -->
<a href='test?a=<?=rawurlencode($test);?>'>test</a>

This issue stems from Apache security updates designed to mitigate critical vulnerabilities. When applications pass URL-encoded question marks (%3F) in query parameters, Apache may interpret the request as malicious and block it with a 403 error.

Root Cause

The behavior is a security measure introduced in Apache versions 2.4.52+ to address two vulnerabilities:

Apache now blocks URLs containing %3F as default behavior to prevent exploit attempts. This breaks URLs built using PHP's rawurlencode() when the content includes question marks.

1. Upgrade Apache and Restructure URLs (Secure Approach)

Best Practice: Upgrade to Apache 2.4.61+, then eliminate %3F from URLs.

php
<?php
// Before: dangerous pattern
$returnUrl = rawurlencode($_GET['return_url']);

// After: safe alternative
$returnPath = rawurlencode(parse_url($_GET['return_url'], PHP_URL_PATH));
$returnParams = http_build_query(['size' => 'large']); // Example params
?>
<a href="login?return_path=<?= $returnPath ?>&return_params=<?= $returnParams ?>">

2. Modify Rewrite Rules (Temporary Workaround)

If immediate URL restructuring isn't feasible, adjust your .htaccess RewriteRules in one of these ways:

apacheconf
# ❌ Problematic rule (triggers 403)
RewriteRule ^(.*)$ index.php?route=$1 [QSA,L]

# ✅ Solution 1 - Remove query from substitution
RewriteRule ^(.*)$ index.php [L]

# ✅ Solution 2 - Add UnsafeAllow3F flag (security risk!)
RewriteRule ^(.*)$ index.php?route=$1 [QSA,L,UnsafeAllow3F]

WARNING

UnsafeAllow3F re-enables vulnerable behavior. Only use this as a short-term mitigation until you finish URL refactoring.

3. Character Replacement Workaround

When refactoring isn't possible, replace question marks before encoding:

php
<?php
// Replace ? with HTML entity before encoding
$safeParam = rawurlencode(str_replace('?', '&#63;', $unsafeInput));
?>
<a href="test?a=<?= $safeParam ?>">Link</a>

Explanation and Best Practices

Security Considerations

DANGER

Using UnsafeAllow3F leaves systems vulnerable to exploitation. Attack vectors will become public at Black Hat USA 2024 - upgrade before then.

  1. Patches First

    • Upgrade to Apache 2.4.61+
    bash
    # Ubuntu/Debian
    sudo apt update && sudo apt install apache2
  2. Restructure URLs

    • Avoid passing encoded URLs as parameters
    • Use distinct param pairs (return_path and return_query)
    • Limit rawurlencode() to path components only
  3. Monitor Logs Check for errors like:

    AH10508: Unsafe URL with %3f URL rewritten without UnsafeAllow3F

Host-Specific Notes

  • Shared hosts like Strato may require support intervention
  • Frameworks like Symfony/CraftCMS have patched default .htaccess files
  • Always test rewrite rule changes in staging environments

Conclusion

The 403 errors stem from necessary security enhancements in Apache. While temporary fixes like UnsafeAllow3F or character replacement exist, the secure solution involves upgrading to Apache 2.4.61+ and restructuring applications to avoid passing %3F in URLs. Prioritize eliminating URL-encoded question marks from parameters to maintain security compliance and application stability.