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.