85

I want to deny direct access to all .php files except one: index.php

The only access to the other .php files should be through php include.

If possible I want all files in the same folder.

UPDATE:

A general rule would be nice, so I don't need to go through all files. The risk is that I forget a file or line.

UPDATE 2:

The index.php is in a folder www.myadress.com/myfolder/index.php

I want to deny access to all .php files in myfolder and subfolders to that folder.

13 Answers 13

112

Are you sure, you want to do that? Even css and js files and images and ...?

OK, first check if mod_access in installed to apache, then add the following to your .htaccess:

Order Deny,Allow
Deny from all
Allow from 127.0.0.1

<Files /index.php>
    Order Allow,Deny
    Allow from all
</Files>

The first directive forbids access to any files except from localhost, because of Order Deny,Allow, Allow gets applied later, the second directive only affects index.php.

Caveat: No space after the comma in the Order line.

To allow access to files matching *.css or *.js use this directive:

<FilesMatch ".*\.(css|js)$">
    Order Allow,Deny
    Allow from all
</FilesMatch>

You cannot use directives for <Location> or <Directory> inside .htaccess files, though.

Your option would be to use <FilesMatch ".*\.php$"> around the first allow,deny group and then explicitely allow access to index.php.

Update for Apache 2.4: This answer is correct for Apache 2.2. In Apache 2.4 the access control paradigm has changed, and the correct syntax is to use Require all denied.

3
  • 3
    Hmm... no, only for .php-files
    – Johan
    Commented Aug 27, 2009 at 10:10
  • Ok, I'm totally novice to .htaccess I tried to copypaste your code, and it works to deny all files except index.php, but not to allow non .php-files. Here is my .htaccess-file: Order Deny,Allow Deny from all Allow from 127.0.0.1 <Files index.php> Order Allow,Deny Allow from all </Files> <FilesMatch "*\.(css|js)$"> Order Allow,Deny Allow from all </FilesMatch>
    – Johan
    Commented Aug 27, 2009 at 10:36
  • 2
    didn't work for me (but I didn't downvote you). I got a 'server misconfigured' message. Perhaps the server is really misconfigured. This worked: stackoverflow.com/a/1607587/539300
    – Tyler
    Commented Aug 31, 2012 at 2:43
91

Actually, I came here with the same question as the creator of the topic, but none of the solutions given were a complete answer to my problem. Why adding a code to ALL the files on your server when you could simply configure it once ? The closest one was Residuum's one, but still, he was excluding ALL files, when I wanted to exclude only php files that weren't named index.php.

So I came up with a .htaccess containing this :

<Files *.php>
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
</Files>

<Files index.php>
    Order Allow,Deny
    Allow from all
</Files>

(Remember, htaccess files are working recursively, so it suits perfectly the prerequisite of the question.)

And here we go. The only php files that will be accessible for a user will be the ones named index.php. But you can still acces to every image, css stylesheet, js script, etc.

35

An oblique answer to the question is to write all the code as classes, apart from the index.php files, which are then the only points of entry. PHP files that contain classes will not cause anything to happen, even if they are invoked directly through Apache.

A direct answer is to include the following in .htaccess:

<FilesMatch "\.php$">
    Order Allow,Deny
    Deny from all
</FilesMatch>
<FilesMatch "index[0-9]?\.php$">
    Order Allow,Deny
    Allow from all
</FilesMatch>

This will allow any file like index.php, index2.php etc to be accessed, but will refuse access of any kind to other .php files. It will not affect other file types.

2
  • If you are using Joomla, Joomla sites usually have only index.php and index2.php. Please note index2.php is deprecated since Joomla 1.6.
    – rineez
    Commented Jul 21, 2014 at 6:47
  • Good stuff, but this is broken when you have other index.php inside your application (which is common in views in some frameworks).
    – Sliq
    Commented Oct 13, 2014 at 19:15
17

You can try defining a constant in index.php and add something like

if (!defined("YOUR_CONSTANT")) die('No direct access');

to the beginning of the other files.

OR, you can use mod_rewrite and redirect requests to index.php, editing .htaccess like this:

RewriteEngine on
RewriteCond $1 !^(index\.php)
RewriteRule ^(.*)$ /index.php/$1 [L,R=301]

Then you should be able to analyze all incoming requests in the index.php and take according actions.

If you want to leave out all *.jpg, *.gif, *.css and *.png files, for example, then you should edit second line like this:

RewriteCond $1 !^(index\.php|*\.jpg|*\.gif|*\.css|*\.png)
6
  • Do you know how? I have no clue.
    – Johan
    Commented Aug 27, 2009 at 10:23
  • Does it matter that index.php is in a folder.
    – Johan
    Commented Aug 27, 2009 at 10:41
  • Yes, you should write full absolute path to it in RewriteRule, like RewriteRule ^(.*)$ /somedir/anotherdir/index.php/$1 [L]
    – n1313
    Commented Aug 27, 2009 at 10:45
  • .htaccess: RewriteRule: unknown flag 'L][R' Commented May 22, 2013 at 12:24
  • Edited: flags should be comma separated Commented May 22, 2013 at 12:33
11

How about keeping all .php-files except for index.php above the web root? No need for any rewrite rules or programmatic kludges.

Adding the includes-folder to your include path will then help to keep things simple, no need to use absolute paths etc.

5
  • Ok, I move my files to another folder. Thanks for all .htaccess help, but it was to complicated for me.
    – Johan
    Commented Aug 27, 2009 at 11:06
  • Or maybe not, it was too many linkreferences between my documents so I wait with this solution.
    – Johan
    Commented Aug 27, 2009 at 11:32
  • What do you mean by linkreference? Did you add the folder you moved the files to into your include path?
    – nikc.org
    Commented Aug 27, 2009 at 12:42
  • Linkreference = include('file.php') to include('myfolder/file.php')
    – Johan
    Commented Aug 27, 2009 at 15:11
  • If you include the folder you move the files into in your include path (ini_set('include_path', ini_get('include_pat'). ':/path/to/files');), then you can do the same thing without anything breaking.
    – nikc.org
    Commented Aug 27, 2009 at 16:25
8

URL rewriting could be used to map a URL to .php files. The following rules can identify whether a .php request was made directly or it was re-written. It forbids the request in the first case:

RewriteEngine On
RewriteCond %{THE_REQUEST} ^.+?\ [^?]+\.php[?\ ]
RewriteRule \.php$ - [F]
RewriteRule test index.php

These rules will forbid all requests that end with .php. However, URLs such as / (which fires index.php), /test (which rewrites to index.php) and /test?f=index.php (which contains index.php in querystring) are still allowed.

THE_REQUEST contains the full HTTP request line sent by the browser to the server (e.g., GET /index.php?foo=bar HTTP/1.1)

2
  • 1
    This is the solution I was looking for. I wanted to deny acccess to all php except my rewrite rules. Thanks! Commented Apr 6, 2013 at 9:48
  • You answer is near what I'm looking for, if you still here may be you can look at stackoverflow.com/questions/57260003/…
    – Serge
    Commented Jul 29, 2019 at 20:44
5

With Apache 2.4, the syntax for access control has changed. If using directives like:

Order deny,allow
Deny from all

does not work, you're likely using Apache 2.4.

The Apache 2.4 docs are here.

You need to use Require all denied in one of these ways:

# you can do it this way (with "not match")

<FilesMatch ^((?!index\.php).)*$>
  Require all denied
</FilesMatch>

# or 

Require all denied

<Files index.php>
  Require all granted
</Files>
1
2

Instead of passing a variable around I do this which is self-contained at the top of any page you don't want direct access to, this should still be paired with .htaccess rules but I feel safer knowing there is a fail-safe if htaccess ever gets messes up.

<?php
// Security check: Deny direct file access; must be loaded through index
if (count(get_included_files()) == 1) {
    header("Location: index.php"); // Send to index
    die("403"); // Must include to stop PHP from continuing
}
?>
1
  • 1
    This is fine for tiny applications, but when you have a load of controllers, models, and templates, etc, then this becomes completely unmanageable.
    – James
    Commented Nov 3, 2014 at 18:23
1

An easy solution is to rename all non-index.php files to .inc, then deny access to *.inc files. I use this in a lot of my projects and it works perfectly fine.

1
<Files ~ "^.*\.([Pp][Hh][Pp])">
 Order allow,deny
 Deny from all
 Satisfy All
</Files>
0

place all files in one folder. place a .htaccess file in that folder and give it the value deny all. then in index.php thats placed outside of the folder have it echo out the right pages based on user input or event

0

Here example from my CMS EFFCORE.

Apache 2.4 notation:

<If "%{REQUEST_URI} -strmatch '*.php'">
    Require all denied
</If>

<If "%{REQUEST_URI} -strmatch '/index.php'">
    Require all granted
</If>

##########################
### SINGLE ENTRY POINT ###
##########################

RewriteEngine On
RewriteRule ^.*$ index.php [L]

NGINX notation:

server {
    listen 127.0.0.1:80;
    server_name 127.0.0.1;
    root /var/www;
    location ~* .*\.php$ {
        deny all;
        error_log off;
    }
  # SINGLE ENTRY POINT
    location / {
        fastcgi_index index.php;
        fastcgi_pass 127.0.0.1:9000;
        include /usr/local/etc/nginx/fastcgi.conf;
        fastcgi_param SCRIPT_NAME /index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
    }
}

IIS 7.5+ notation:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="*.php files protection" patternSyntax="Wildcard" stopProcessing="true">
                    <match url="*.php" />
                    <action type="CustomResponse" statusCode="403" subStatusCode="0" statusReason="Forbidden" statusDescription="Forbidden" />
                </rule>
                <rule name="SINGLE ENTRY POINT" patternSyntax="ECMAScript" stopProcessing="true">
                    <match url="^.*$" ignoreCase="true" />
                    <action type="Rewrite" url="index.php/{R:0}" appendQueryString="true" />
                </rule>
            </rules>
        </rewrite>
        <defaultDocument>
            <files>
                <clear />
                <add value="index.php" />
            </files>
        </defaultDocument>
    </system.webServer>
</configuration>
-3

Allow only 2 ip , all other will block

Order Deny,Allow
Deny from all
Allow from 173.11.227.73 108.222.245.179

Not the answer you're looking for? Browse other questions tagged or ask your own question.