.htaccessmod-rewriteconfigurationconfigapache2.4

How to Allow Access to Only Specific URLs and Rewrite Them Using Apache .htaccess?


I'm working with Apache and want to use .htaccess to achieve the following:

Essentially, I want to allow only these two specific endpoints (/api/get and /api/report), and prevent access to anything else.

What ive tried:

RewriteEngine on

RewriteRule ^api/get$ /get.php [L]
RewriteRule ^api/report$ /report.php [L]

Order deny,allow
Deny from all

<Files "get.php">
    Allow from all
</Files>

<Files "report.php">
    Allow from all
</Files>

Resulting in access being denied to everything except /get.php and /report.php

RewriteEngine on

RewriteRule ^api/get$ /get.php [L]
RewriteRule ^api/report$ /report.php [L]
RewriteRule ^ - [F]

Leading to complete access denial


Solution

  • You can do it like this on Apache 2.4:

    RewriteEngine on
    
    # Rewrite requests to "/api/get" and "/api/report"
    RewriteRule ^api/(get|report)$ $1.php [END]
    
    # Block everything else, including direct requests to "/get.php" and "/report.php"
    RewriteRule ^ - [F]
    

    Note the use of the END flag (as opposed to L) on the first rule (requires Apache 2.4+). This prevents any further rules being processed (for the request) should this rule be met. (An L flag simply prevents additional rules being processed during the current pass only by the rewrite engine.)

    The $1 backreference contains either get or report as captured from the initial request URL. This allows your two rules to be easily combined.

    No need for the slash-prefix on the RewriteRule substitution (2nd argument) in the above rule. This ensures we rewrite directly to a file-path, as opposed to a URL-path (although it would get there in the end).


    Aside:

    RewriteRule ^api/get$ /get.php [L]
    RewriteRule ^api/report$ /report.php [L]
    RewriteRule ^ - [F]
    

    This is close, but without an additional condition on the last rule, the use of the L flag on the first two rules would result in all requests being blocked.

    If on Apache 2.2, this could be written like this, making use of the REDIRECT_STATUS environment variable:

    RewriteRule ^api/get$ /get.php [L]
    RewriteRule ^api/report$ /report.php [L]
    
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ^ - [F]
    

    The use of the REDIRECT_STATUS env var ensures that only direct requests (from the client) are processed (ie. blocked) and not rewritten requests by the previous two rules. On the initial pass by the rewrite engine, the REDIRECT_STATUS var is empty, but after the first successful internal rewrite it is set to 200 (as in a 200 OK HTTP status).

    RewriteCond %{REQUEST_URI} !^/api/get$ [OR]
    RewriteCond %{REQUEST_URI} !^/api/report$
    RewriteRule ^ - [F]
    
    RewriteRule ^api/get$ get.php [L]
    RewriteRule ^api/report$ report.php [L]
    

    As mentioned in comments, the logic is incorrect on the first rule. If you OR two negated mutually exclusive expressions the result will always be true and the request is always blocked.

    However, the request would still be blocked for the same reason as mentioned above.