nycki.net/content/blog/27-apache-slashy.md
nycki 7381e792a8
All checks were successful
/ build (push) Successful in 48s
blog 27: removing slashes and stuff in apache2
2025-10-30 13:05:12 -07:00

3.2 KiB

date title description preview_image tags draft permalink
2025-10-30 Apache Slash-y remove trailing /index.php from URIs with apache web server. programming true /blog/27/

gahhhhhh this was driving me crazy, putting it here so i don't lose it

basically this solves the problem where you have pages like

http://example.com/page2/index.php?query=true

and you want it to display in the browser as the much nicer

http://example.com/page2?query=true

and you can't use nginx try_files because you promised yourself you were gonna make this work in apache "for old times' sake"

basically: this takes a surprisingly large number of rewrite rules. in order:

  • remove trailing slashes, page2/ -> page2
  • remove file extensions, page2.php -> page2
  • remove index filenames, page2/index.php -> page2
  • oh yeah for EACH of those last three things, I hope you remembered to preserve the query string, so page2/?query=true -> page2?query=true
  • okay now you have a nice short URL in the browser. now just undo all that logic to get the actual php file to run on the server.
  • if page2 is a directory then serve from page2/index.php
  • if page2.php exists then serve from page2.php
  • if none of those things exist then fall back on static file hosting. like, page2.html might exist, you can serve that, sure, why not.
  • oh you wanted to remove the html extension too? okay do all that again bozo.
RewriteEngine On
#LogLevel alert rewrite:trace6

# remove trailing slash
RewriteRule (\/.*)\/+(\?.*)?$ $1$2 [R]

# remove file extensions
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
RewriteRule (.*)\.(php|html)(\?.*)?$ $1$3 [NC,R]

# remove index.php index.html etc
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule (.*)\/index(\?.*)?$ $1$2 [NC,R]

# serve php file
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule (\?.*)?$ fcgi://php:9000/usr/local/apache2/htdocs/%{REQUEST_URI}.php$1 [P,L]

# serve php file with index
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/ -d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/index.php -f
RewriteRule (\?.*)?$ fcgi://php:9000/usr/local/apache2/htdocs/%{REQUEST_URI}/index.php$1 [P,L]

# serve html file
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f
RewriteRule ([^?]*)(\?.*)?$ $1.html$2 [L]

# serve html file with index
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/ -d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}/index.html -f
RewriteRule ([^?]*)(\?.*)?$ $1/index.html$2 [L]

some useful tricks:

  • [NC] can be added after any regex rule to make it Non-Case sensitive.
  • [R] means "in-browser redirect" and is used when you want to fix a URI on the user's side. if you don't do this then it only affects where the server pulls data from.
  • [P] means "proxy" and it allows you to have the server pull data from another machine or from a program running on a local port.
  • [L] means "last one" and it skips all remaining rewrite rules, in case you were worried about matching too many of them.

all this and more can be found at the apache2 docs.

if there's an easy way to do all this then please please tell me, I miss try_files so much, holy cow.