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

81 lines
No EOL
3.2 KiB
Markdown

---
date: 2025-10-30
title: Apache Slash-y
description: remove trailing /index.php from URIs with apache web server.
preview_image:
tags: programming
draft: true
permalink: /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.
```sh
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](https://httpd.apache.org/docs/current/mod/mod_rewrite.html).
if there's an easy way to do all this then please please tell me, I miss try_files so much, holy cow.