what prevents writing a file (with correct permissions and ownership)

I'm simply trying to write a file, using fopen and fwrite.

Permissions are 775, ownership is me:apache. (I'm the only user.)

I get a message essentially "file is not writeable". If it matters in this case, php.ini's allow-url-open is "on".

I read the following in a stackoverflow post, and wonder if this is the answer. I'm reluctant to do this without understanding the problem and if this is safe:

At last reached the answer myself! I had to modify the SELinux configuration with chon command and apply permission as:httpd_sys_rw_content_t

chcon -R -h -t httpd_sys_rw_content_t /var/www/data/
along with:

chown -R apache:apache/var/www/data/
chmod -R a+rX /var/www/data/

What else controls whether a file is writeable?

13 Replies

This may or may not apply to you, but my experience has always been that, in order for a file to be writable by php and apache, not only does the file need to be owned and writable by the web server but the directory containing the file must be owned and writable bythe web server as well.

I have run into this a lot with SQLite databases (which are files). As soon as I get the SQLite error about trying to write to a read-only database, that usually triggers my memory…

My solution is to usually make the file/directory group-writable (0775) with ownership of apache/apache. That will still prevent some random bozo or malicious user from writing to the file (mistakenly or intentionally). If you're using php-fpm to serve your php-driven content to apache, php-fpm runs as the web server.

-- sw

Let's walk through it:

chcon -R -h -t httpd_sys_rw_content_t /var/www/data/

The chcon lets you change the SELinux security context of files.

SELinux is another layer of security on top of regular linux permissions. You can define rules that not only what users/processes can modify files but in what contexts they can be modified.

Let's take a look at the definition of httpd_sys_rw_content_t

Files labeled with this type can be written to by scripts labeled with the httpd_sys_script_exec_t type, but cannot be modified by scripts labeled with any other type. You must use the httpd_sys_rw_content_t type to label files that will be read from and written to by scripts labeled with the httpd_sys_script_exec_t type.`

What does this mean? Well, processes will have contexts too. You can see for yourself using ps auxZ. Your PHP scripts will likely be running with the as the apache user with the httpd_sys_script_exec_t context.

The punchline is: Scripts run by Apache only modify files with the httpd_sys_rw_content_t label. Any other files that apache may own (let's say logs), will be protected.

The rules are basically:

PHP, being a script run by httpd, may only modify files specifically meant to be modified by scripts run by httpd.

The command you are executing is saying "all the files and folders under /var/www/data/ are meant to be read and written by scripts run by httpd".

If that's your intent, then it is safe to execute.

You can find a nice introduction to SELinux here

You can also disable it if you find it too burdensome, at the cost of the security it provides. If this is for personal stuff then do what makes you happy

As far the the last two commands:

chown -R apache:apache /var/www/data/

This will change the ownership to apache, you shouldn't need to do this if the files are group read/writable and directories are group read/write/executable (g+rwX). It's up to you if that's what you want to do vs keeping yourself as the owner

chmod -R a+rX /var/www/data/

This will make all files/directories readable by all users

a change privileges for user, group, and other
+ add to existing privileges
r read
X listing directories (sets executable bit on directories only)


Your discussion is right on the nose. I have no problem with apache:apache. And if I'm in the apache group, I still have the group permissions, and they are the same for user and group. I use permissions 775 for the containing directory (called see below) and all folders/files within. I am the only 'user'. So I think all is well regarding ownership and permissions.

I have some questions:

  1. The files I want publicly readable are in this path:


Does what you say only apply to /var/www/data/ or, in my case, can it be used like this

chcon -R -h -t httpd_sys_rw_content_t /srv/www/my_application_domain/public_html/see/

  1. What to you mean when you say scripts/files need to be "labelled" with httpd_sys_rw_content_t . I'm not familiar with the label concept (hmmm, how have I lived so long using Apache without knowing that). How do I do that labelling?

  2. Just being super cautious: I do not want the public to write those files. I trust that permissions 775 guarantees that no matter what else may be going on. Please confirm.

  3. You say that I shouldn't need to do chown -R apache:apache if, paraphrasing, the files are permission 755 and the directory containing those files is 775. In my case, they are both 775, and that meets your criteria … I think. :) But is that a recommendation versus ownership being me:apache? Is one way "better" in some way?

I want to say you are a LIFESAVER! You don't know how many hours and much coffee and late hours I've invested in getting an answer, OMG.

stevewi, thank you for taking the time to help. I find what tarq says is the answer I need.

tarq (and steve wi),

Second thoughts:

If I don't have SELinux installed, then why isn't ability to write a file totally determined by Ownership and Permissions?

I'm running CentOS7. The original stsckoverflow post I read that started our discussion was about a case where SELinux was installed.

I hate to say this, but I think I'm back to square one.

I have Apache:Apache ownership and 775 permissions for the overall containing folder for all the folders/files, and for all those folders and files.

So, the first thing is that data files (especially ones you write) don't need to be executable, so the permissions you want for files is 644 (-rw-r--r--). Second, the directory you want to write data files needs to be writable by the process owner/group, so generally that means 755 (-rwxr-xr-x). Third, if you're going to create a data file, the owner/group of the directory you want to write into needs to match the owner/group of the process doing the writing (you say apache:apache in your case). The owner/group of the file and the directory in which it lives needs to match the owner/group of the process doing the writing if you want to update the file.

Making everything 775 is probably overkill and may be the security hole you're trying to avoid. Generally, unless specifically required (contents of cgi-bin), you don't need execute permission on anything but directories…not even *.php files! PHP is an interpreter…all it needs to run PHP programs is to be able to READ those files…all the executive action is taken by the PHP interpreter (which is a program and not just a data file).

PHP is also kind of a weird duck in that, there's also PHP options in /etc/php.ini that govern this sort of thing as well. These are intended to be "extra protection" from shooting yourself in the foot. You need to investigate these and make sure they're set properly.

Apache has its own, separate notion of where it can write files as well. You have to configure/tell apache it can do this (usually on a case-by-case basis).

I haven't used PHP for a long time so I haven't really kept up with how it's changed. These were the rules for PHP 7.0. I'm afraid you're going to have to do some more digging. Maybe someone else knows the answer to this (@acanton77 or @andysh ?).

-- sw

These links may be of use to you:




Thank you. Your comment "Apache has its own, separate notion of where it can write files as well…" caught my attention. I don't know what/where to look in httpd config file (assuming that's where need to investigate).

I got some digging to do, unless someone else comes along and tells me specifically where Apache controls writing files. Case-by-case works -- all the files are in one folder, so I can apply whatever it is to all files in that folder, and adjust folder appropriately too.

The "funny" thing is, this all worked fine a month ago. I don't know what changed since then. I am confident that Linode will not change anything at regarding software I'm running, and overall server configuration. It must I unwittingly changed. What else could it be.

I do not fool around with configurations and software version as some kind of perverse hobby. 🥴

I do not fool around with configurations and software version as some kind of perverse hobby. 🥴

Nor do I but apache is patched frequently (just a side note, "apache" is a normalization of "a patchy"). Because of it's widespread use and it's nature as an internet server, security holes and bugs are uncovered frequently.

You may have installed an update that fixed a bug you were taking advantage of…

-- sw


You raise an interesting point. I've been living with the belief that software will not be updated by Linode, i.e., the configuration is frozen unless I make a change. That is true for example to updating the php package. But it must be false when it comes to CentOS-7 and Apache … no?

I've been talking to myself, which I do more and more 😳, saying, "What happened, I didn't make any changes and _ used to work just fine."


Apache has its own, separate notion of where it can write files as well. You have to configure/tell apache it can do this (usually on a case-by-case basis).

I looked at httpd conf and, frankly, my eyes glossed over as I tried to find something that that had to do with controlling file writing.

I would definitely appreciate pointers about that from anyone who stumbles across this thread.

That is true for example to updating the php package. But it must be false when it comes to CentOS-7 and Apache … no?

Linode do not have access to your Linode itself, so any updates to the OS must be done by yourself (or a management tool.)

That being said - if you run a Linode kernel (in your Linode’s Configuration section in Manager, something other than GRUB 2 is selected for the kernel in your configuration profile) then a reboot could trigger a new kernel version to be deployed.

I’m jumping in this thread a bit late, so apologies, but I couldn’t work if you do have SELinux enabled? I believe CentOS 7 has it enabled by default, unless you are running a Linode kernel (see above) which does not support it.

What does sestatus tell you?

Again, apologies if this has been covered already, but are you sure your PHP process is running under the user/group you believe it is?

Is it php-fpm or the PHP module loaded within Apache? What does ps -e -o user,group,comm|grep php-fpm turn up?

Here's an example of apache2 config that is known to work and allows updates to an sqlite3 database at /srv/ical/var/db/davsyncdb.sqlite:

<VirtualHost _default_:443>

    # Admin email, Server Name (domain name), and any aliases
    ServerAdmin postmaster@mydomain.com
    ServerName mydomain.com
    ServerSignature Off

    Include /path/to/letsencrypt/options-ssl-apache.conf

    SSLCertificateFile /path/to/letsencrypt/live/mydomain.com/cert.pem
    SSLCertificateKeyFile /path/to/letsencrypt/live/mydomain.com/privkey.pem
    SSLCACertificateFile /path/to/letsencrypt/live/mydomain.com/chain.pem

    # Index file and Document Root (where the public files are located)
    DirectoryIndex index.php.   # <-- The "index" file -- this is a php web app
    DocumentRoot /srv/ical/www. # <-- This applies to the current virtual host only

    Alias "/i" "/srv/ical/www"  # <-- create an alias (this means I can use URLs like:
                                #        "https://mydomain.com/i/..."
                                #     when I mean 
                                #        "https://mydomain.com/srv/ical/www/..."

    <Directory /srv/ical/www>
        SSLOptions +StdEnvVars  # <-- set up SSL (need mod_ssl for this)
        AllowOverride None      # <-- .htaccess can't override anything
        Options -Indexes        # <-- Don't show any directory indexes
        Require all granted     # <-- I suspect this may be the piece you're missing

        # engage mod_rewrite
        RewriteEngine On

        # Only certain HTTP methods are allowed here for security
        RewriteRule .* - [F]

        # Add the authorization header
        RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]



The updates are done with a php script run with php-fpm (the process has www:www ownership (see below).

Here's the permissions (starting at /srv/ical/var…pay attention to '.' & '..' ownership/permissions):

total 10
drwxr-xr-x  4 stevewi  stevewi   4 Aug  2  2021 .
drwxr-xr-x  9 stevewi  stevewi  11 Aug  2  2021 ..
drwxr-xr-x  2 www      www       4 Feb  8 07:35 db
drwxr-xr-x  2 stevewi  stevewi   4 Feb  8 00:00 dump

total 11078
drwxr-xr-x  2 www      www             4 Feb  8 07:35 .
drwxr-xr-x  4 stevewi  stevewi         4 Aug  2  2021 ..
-rw-r--r--  1 www      www             0 Aug  2  2021 .gitkeep
-rw-r--r--  1 www      www      19005440 Feb  8 07:35 davsyncdb.sqlite

total 11370
drwxr-xr-x  2 stevewi  stevewi         4 Feb  8 00:00 .
drwxr-xr-x  4 stevewi  stevewi         4 Aug  2  2021 ..
-rw-r--r--  1 stevewi  stevewi         0 Aug  2  2021 .gitkeep
-rw-r--r--  1 stevewi  stevewi  13182206 Feb  8 00:00 davsyncdb.dump

www is the web server user/group (equivalent to apache:apache in your world). davsyncdb.dump is an sqlite3 dump created by a cron job every night.

Here's how php is enabled (in /etc/apache2/conf-enabled):

# Comments: Redirect to local php-fpm.
# Depends: module/mod_proxy module/mod_proxy_fcgi
<IfModule proxy_fcgi_module>

    # Enable http authorization headers
    <IfModule setenvif_module>
        SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1

    <FilesMatch ".+\.ph(ar|p|tml)$">
        SetHandler "proxy:unix:/var/run/php-fpm.sock|fcgi://localhost"

    <FilesMatch ".+\.phps$">

        # Deny access to raw php sources by default.
        # To re-enable it's recommended to enable access to the 
        # files only in a specific virtual host or directory
        Require all denied


    # Deny access to files without filename (e.g. '.php')
    <FilesMatch "^\.ph(ar|p|ps|tml)$">
        Require all denied


Lastly, here's the Apache documentation on the Require directive:


-- sw

Thanks for the help, everyone. Usually, when I have a problem with writing important projects, I turn to https://www.aresearchguide.com/research-paper-and-outline-format.html where I can get prompt help from professionals and complete my academic work on time.

this type of shit makes me want to just use bluehost or something. massive waste of time


Please enter an answer

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct