Search code examples
phpapache.htaccessmod-rewritemod-alias

Apache Redirect /api Requests To Directory


I come from a C# MVC background but I am currently working on a PHP project.

In C# you can setup decorators to specify the request URL using something along these lines:

[HttpGet]
[Route("/ControllerName/ActionName")]
public string MyAction() {
    // ...
}

I am wanting to set something similar up in PHP, so I set my file structure like this:

+ assets
  + css
  + javascript
  + images
+ server
  + api
    + controllers
    - index.php
+ ui
  + area1
    - view1.html
    - view2.html
  + area2
    - etc.html
- .htaccess
- index.html

Where index.html is my login page, the other html pages live in their respective /ui/area directory, and my restful API pages live in /server/api/controllers. The /server/api/index.php follows a very similar pattern to this Stack Overflow answer: https://stackoverflow.com/a/1737903/1920035

In my .htaccess file, I have the following redirect rule setup:

# Redirect traffic starting with /api to the /server/api/index.php file
Redirect 301 ^(api/.*)$ /server/api/index.php [L,QSA]

The idea is that if I hit /api/account/get that it will redirect to the /server/api/index.php file and run its magic to run the respective controller/action.

The issue is that when I spin up my server and hit the pretty URL that it always returns a 500 response without any useful response text.

To complicate things, I'm running Docker for Windows using php:8.2.2-apache.

What am I doing wrong here? I do not have much experience setting up .htaccess files and with a vague 500 response, I don't have much to go off of.


Solution

  • You need to check Apache's error log for the details of the 500 error. However...

    Redirect 301 ^(api/.*)$ /server/api/index.php [L,QSA]
    

    This is syntactically invalid (which might account for the 500 error) and the wrong directive to use here. Redirect is a mod_alias directive that triggers an external redirect. [L,QSA] are "flags" used with the mod_rewrite RewriteRule directive. The Redirect directive does not take a regex as an argument (it uses simple prefix-matching).

    But you shouldn't be triggering an HTTP external (301) redirect here, you need to internally rewrite the request instead (the user should not be aware of the underlying /server subdirectory).

    For this (URL rewriting) you need to use mod_rewrite (so this needs to be installed if not already). For example:

    RewriteEngine On
    
    RewriteRule ^api/ server/api/index.php [L]
    

    A request for /api/<anything> would be internally rewritten to /server/api/index.php.

    No need for the capturing group and .* suffix on the original regex. So, ^api/ is essentially the same as ^(api/.*)$, but more efficient. The slash prefix on the substitution string (2nd argument) is not required with internal rewrites (and best omitted - so it is treated as a filesystem path and not a URL-path).

    mod_rewrite (RewriteRule / RewriteCond directives) is capable of issuing (complex) external redirects as well as internal rewrites. mod_alias (Redirect / RedirectMatch directives) is for simple external redirects only. (Having said that, mod_alias also has some directives used for internal filesystem mapping.)

    Reference: