Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Argelbargel/eea6dc1b3d986d80c3843d3375c78365 to your computer and use it in GitHub Desktop.
Save Argelbargel/eea6dc1b3d986d80c3843d3375c78365 to your computer and use it in GitHub Desktop.
How to use Apache 2.4+ as proxy for Rest-Service which allows encoded slashes (%2F)
######################################################################################################################################
# When to use:
# You have a REST-Service which accepts requests like /endpoint/id%2Fwith%2Fslashes and you want to proxy it through Apache,
# either to load-balance multiple server or to add authentication to a service you can not modify for that.
#
# Resolves these problems:
# - Apache2 prevents requests to URIs containing encoded slashes by default and rejects them by returning 404 Not Found
# - Additionally mod_proxy canonicalizes URLs replacing %2F with %252F before passing the request to the backend
# - while Apache 2.4 added the special keyword "nocanon" to the ProxyPass directive there is a security feature built-in that
# enforces canoncialisation when the incoming request URL matching the given prefix (e.g. "/") contains an unescaped path
# (indicated by [proxy:warn] AH01136: Unescaped URL path matched ProxyPass; ignoring unsafe nocanon in the error log)
# - ProxyMatch does not have the keyword "nocanon" so when matching by pattern canonicalisation is always enforced
#
# How it works:
# Instead of using ProxyPass or ProxyMatch this config-file uses rewrite-rules to pass matching requests to mod_proxy without it
# interfering with the URL passed to the backend. To prevent any modification by apache the path is captured from the raw request
# line using a rewrite-condition
#
# Caveats:
# - this disables reasonable and generally useful security-features. Ensure that the backend validates the passed request-uris!
# - this example only contains the minimal directives, omiting boiler-plate like log-configuration, authentication etc.
#
# Useful links:
# - https://httpd.apache.org/docs/2.4/mod/core.html#allowencodedslashes
# - https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#proxypass
# - https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
# - https://httpd.apache.org/docs/2.4/rewrite/flags.html#flag_p
# - https://stackoverflow.com/ and https://serverfault.com/
# (while they did not provide me the answer in this case, they did thousands of times before ;-)
##################################################################################################################################
# Required modules (remove if statically compiled into your apache-installation)
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule rewrite_module modules/mod_rewrite.so
# ensures that encoded slashes are accepted and passed as is
AllowEncodedSlashes NoDecode
# rewrite-rules selecting requests to pass to the backend
RewriteEngine on
# rules for requests that should not be proxied must come first (apache's server-status in my case)
# RewriteCond %{SCRIPT_FILENAME} ^/server-status
# RewriteRule ".*" - [PT,L]
# followed by rule(s) sending requests to the backend. This example passes everything not excluded above to the backend.
RewriteCond %{THE_REQUEST} ^[^/]+/+([^?&#\s]*)
RewriteRule "(.*)" "http://<your-backend>/%1" [NE,P]
ProxyPassReverse "/" "http://<your-backend>/"
# if your case is really simple you could use a single rule like this one instead of the above
# RewriteCond %{THE_REQUEST} ^[^/]+/+(?!server-status)([^?&#\s]*)
# RewriteRule "(.*)" "http://<your-backend>/%1" [NE,P]
# ProxyPassReverse-Rules matching the above rewrite-rules
ProxyPassReverse "/" "http://<your-backend>/"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment