Fixing 403 Forbidden Errors
A 403 Forbidden response means something between the visitor and your page understood the request and decided to refuse it. That something might be the operating system refusing to let the web server read a file, a deny rule in the server configuration, a security plugin, or a firewall at your CDN.
Visitors see a blank refusal, and search engines drop pages they cannot fetch. This guide shows how to work out which layer said no, then how to fix permissions, rules and bot protection without opening up anything that should stay private.
What a 403 actually tells you
A 403 is a decision, not a crash. The server received the request, knew what was being asked for and chose not to serve it. That makes it different from its neighbours:
| Code | What it says | What the visitor can do |
|---|---|---|
| 401 Unauthorized | You need to log in first. The response should ask for credentials. | Sign in and try again. |
| 403 Forbidden | I know what you want and I will not give it to you, even if you log in. | Nothing, unless the site owner changes a rule. |
| 404 Not Found | There is nothing here, or I am not telling you whether there is. | Check the address. |
| 429 Too Many Requests | Slow down; you have sent too many requests. | Wait and retry later. |
Because a 403 is a deliberate refusal, the fix is always to find the rule or setting that made the decision. Almost every 403 comes from one of five layers, and each has its own tell-tale sign:
- The file system. The web server process is not allowed to read the file or enter the folder. Every file under that folder usually fails the same way.
- The web server configuration. A
Require,Denyordenydirective, or a folder with no index file and directory listing turned off. - The application. WordPress, Laravel or a similar app returns 403 itself, often for a failed security token, a blocked user role or a plugin rule.
- A web application firewall. ModSecurity, Imunify360, Wordfence or similar rules match something in the request, such as a query string or user agent.
- The CDN or edge. Cloudflare, a load balancer or a hosting firewall blocks the request before it reaches your server at all.
Working from the outside in, starting with the CDN and ending with the file system, is quicker than guessing, because the outer layers leave obvious fingerprints in the response.
Finding the layer that refused
Start with the response itself. Request the failing URL from a terminal and look at the headers and the body, not just the status code.
curl -sI https://www.example.co.uk/brochure/price-list.pdf
curl -s https://www.example.co.uk/brochure/price-list.pdf | head -30
- A
server: cloudflareheader with acf-rayvalue, and a Cloudflare-branded body, means the edge blocked it. Look in the Cloudflare security events for that Ray ID. - A body mentioning ModSecurity, Imunify360, a hosting provider's firewall, or a reference number, points to a web application firewall on the server.
- A plain page reading "You don't have permission to access this resource" is the standard Apache refusal. It comes from the configuration or the file system.
- A page styled like your own site, with your header and footer, came from the application.
Then check whether the refusal depends on who is asking. Many 403s in an audit only happen to automated clients.
# As a normal browser
curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" https://www.example.co.uk/services/
# As a bare script
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.co.uk/services/
# Claiming to be Googlebot
curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" https://www.example.co.uk/services/
If the browser string gets 200 and the others get 403, a bot rule is responsible. Note that a request claiming to be Googlebot from your own machine is often blocked on purpose, because good firewalls verify that Googlebot really comes from Google. That is correct behaviour, so test from the crawler's point of view with the URL Inspection tool in Search Console instead.
Log lines worth recognising
| Log message | Meaning |
|---|---|
AH01630: client denied by server configuration | An Apache Require
or older Deny from rule refused the request. |
(13)Permission denied with AH00132 or AH00035 | Apache could not read the file or search a parent folder. |
pcfg_openfile: unable to check htaccess file | Apache could not read an .htaccess
file, usually because of folder permissions. |
AH01276: Cannot serve directory ... No matching DirectoryIndex | A folder was requested, there is no index file, and listing is off. |
directory index of "/var/www/..." is forbidden | The nginx version of the same problem. |
open() "..." failed (13: Permission denied) | nginx cannot read the file. |
access forbidden by rule | An nginx deny directive matched. |
ModSecurity: Access denied with code 403 | A ModSecurity rule matched. The line includes the rule id. |
If the web server log has no entry at all for the request, it never reached the server and the block is upstream, at the CDN, the load balancer or a network firewall.
Permissions and ownership
On Linux hosting, the web server runs as a particular user, and it can only serve files that user
is allowed to read. It also needs the execute bit on every folder along the path, which is what allows
a process to pass through a folder. A single parent folder set to 700 can make everything
beneath it forbidden.
A common and safe starting point for a typical PHP site is folders at 755 and files at
644, with the files owned by the site's own user. Some managed hosts use tighter settings,
such as 750 on the web root with a shared group, so follow your host's documentation where
it differs.
# Show the permissions on every folder from the root down to the file
namei -l /var/www/example/public/brochure/price-list.pdf
# Reset folders to 755 and files to 644 inside the web root
find /var/www/example/public -type d -exec chmod 755 {} +
find /var/www/example/public -type f -exec chmod 644 {} +
# Give the files back to the site user if an upload or root copy changed them
chown -R exampleuser:examplegroup /var/www/example/public
chmod 777. It lets any process on the server
change your files, and some hosting setups refuse to run PHP from world-writable locations, so it can
swap a 403 for a 500.- Files uploaded as root. Files copied in over SSH as the root user, or extracted from an archive, often end up owned by root with restrictive modes. The folder listing looks fine but the web server cannot open the files.
- Configuration files you want private. A file such as
wp-config.phpor.envmay deliberately be tighter, for example600or640. That is fine as long as the PHP process still runs as a user that can read it. - SELinux. On servers with SELinux enforcing, correct Unix permissions are not enough;
the files also need the right security context. If
ls -Zshows an unexpected type on files you moved into the web root,restorecon -Ron that folder usually resolves it. - Symbolic links. Apache only follows links when
Options FollowSymLinksorSymLinksIfOwnerMatchallows it, and the second option refuses links whose target has a different owner.
Configuration rules that refuse requests
Access rules are meant to refuse some requests. The problem is a rule that matches more than intended.
Look for these patterns in the server configuration and in every .htaccess file from the
web root down to the failing folder, because Apache applies them all.
# Apache 2.4: refuses everything in this folder and below
Require all denied
# Older Apache 2.2 syntax that still appears in copied snippets
Order deny,allow
Deny from all
# A narrow rule that only protects dotfiles such as .env and .git
<FilesMatch "^\.">
Require all denied
</FilesMatch>
A Deny from all block copied into the wrong folder, such as an uploads folder that also
holds public PDFs, will forbid every file in it. Mixing the old Order and Deny
syntax with the Apache 2.4 Require syntax in the same place can also behave in surprising
ways, so rewrite older snippets in the 2.4 form.
The nginx equivalents live in the server block. A location that is too broad, such as
one meant to hide dotfiles, can catch real pages:
# Too broad: any path containing a dot after a slash, including /.well-known/
location ~ /\. { deny all; }
# Better: keep /.well-known/ open for certificate checks and similar files
location ^~ /.well-known/ { allow all; }
location ~ /\.(?!well-known) { deny all; }
Folders with no index file
If someone visits /services/ and there is no index.html or index.php
in that folder, the server can either list the files or refuse. On most hosts listing is turned off,
so the visitor gets a 403. The fix is to add an index file, to point the link at the real page, or to
add a redirect. Do not turn directory listing back on to make the error go away; it exposes every file
in the folder.
Hotlink protection and referrer rules
Rules that block images or files unless the referrer is your own domain will return 403 to anyone who opens the file directly, to social networks building previews and to search engine image crawlers. Allow empty referrers and the crawlers you care about, or remove the rule if bandwidth is not a real concern.
Firewalls, bot protection and crawlers
Web application firewalls block requests that look like attacks. They are useful, but generic rule sets are cautious and sometimes refuse normal traffic. Typical false positives include search pages with unusual characters in the query string, form posts containing code snippets or HTML, long tracking parameters and uncommon user agents.
- ModSecurity. Find the rule id in the error log or audit log, then disable that single rule for the affected path rather than turning ModSecurity off for the whole site.
- Security plugins. Wordfence, Solid Security and similar plugins keep their own block logs and country or rate rules. A crawler that requests many pages quickly can be blocked for a period by rate limits meant for attackers.
- Cloudflare and other CDNs. Bot Fight Mode, custom WAF rules and country blocks all return 403 before the request reaches you. The security events log shows which rule matched.
- Hosting firewalls. Some hosts run their own firewall in front of shared servers. If there is no trace anywhere you can see, send your host the URL, the time and your audit's IP address and ask them to check.
Why this matters for search
Google does not index a URL that returns 403, and a site-wide 403 for Googlebot looks like the whole site has disappeared. Google also advises against using 403 or 404 to slow its crawling; if you need to reduce crawl load temporarily, it recommends 429 or 503 responses, which it treats as a signal to back off rather than as a refusal. Allow verified search engine crawlers by checking reverse DNS or the IP ranges they publish, not by trusting the user agent, which anyone can fake.
The fix, step by step
Step 1
Confirm the 403 and who sent it
Take
a failing URL from the HTTP Error Audit and request it with curl -sI, then look at the body
as well. A cf-ray header or a CDN-branded page means the edge refused it; a firewall reference
number points to ModSecurity or a similar tool; a plain Apache or nginx refusal means the server configuration
or file system. Repeat with a browser user agent to see whether only automated clients are blocked.
Step 2
Read the matching log line
Search
the web server error log for the path at the time of the request. client denied by server configuration
or access forbidden by rule means a deny rule, Permission denied means file
access, No matching DirectoryIndex or directory index is forbidden means a
missing index file, and a ModSecurity line gives a rule id. No line at all means the block happened upstream
at the CDN or a network firewall.
Step 3
Correct ownership and permissions
Run
namei -l on the full file path to see every folder above it, and make sure each has the
execute bit for the web server. Set folders to 755 and files to 644 unless
your host documents something tighter, and give ownership back to the site user if files were copied
in as root. Never use 777. On SELinux servers, restore the file contexts with restorecon
as well.
Step 4
Fix index files and directory listing
If
the refused address ends in a slash and points at a folder, check that the folder contains an index.html
or index.php and that DirectoryIndex or the nginx index directive
lists that file name. Add the missing file or redirect the folder address to the real page. Leave directory
listing switched off, because turning it on exposes every file in the folder to anyone.
Step 5
Narrow the deny rule that caught it
Open every .htaccess file from the web root down to the failing folder, plus the
virtual host or nginx server block, and find the Require all denied, Deny from all
or deny all that matches. Rewrite it so it only covers what must stay private, such as dotfiles
or a config folder, keep /.well-known/ open, and convert old Apache 2.2 syntax to the 2.4
form.
Step 6
Allow real crawlers through the firewall
In the CDN security events, the ModSecurity log or the security plugin's block list, find the rule that matched the audit or the search engine crawler. Add a narrow exception for that rule and path, or allow verified crawlers by reverse DNS or their published IP ranges rather than by user agent. Check country blocks and rate limits too, since a fast crawl can trip them.
Step 7
Retest as a browser and as a crawler
Purge
any CDN cache for the address, then request it again with curl using both a browser user agent and a
plain one, and confirm a 200 each time. Use URL Inspection in Search Console to run a live
test as Googlebot. Finally, re-run the HTTP Error Audit so every URL that shared the same cause is checked,
not only the one you tested.
Platform notes
- WordPress. A 403 on
/wp-admin/or when saving a post is often a security plugin or ModSecurity reacting to the content being saved. A 403 on every page after a plugin install usually means the plugin wrote rules into.htaccess. The WordPress HTTP errors guide covers plugin conflicts in more detail. - Plesk and cPanel. Both panels can reset permissions and both show ModSecurity hits in their own interfaces. The Plesk and cPanel guide explains where those tools live.
- Static hosts and object storage. A 403 from a storage bucket or static host usually means the object is not public, or the address points at a folder with no index document configured. Many of these services return 403 rather than 404 for missing files, so check that the file really exists.
- Frameworks. Laravel, Django and Rails return 403 for failed CSRF checks and authorisation policies. These appear in the application log, not the web server log, and usually affect form submissions rather than page views.
Keeping 403s from coming back
- Deploy with the right user. Upload and deploy as the site user, not root, so ownership stays consistent. If a deploy script must run as root, have it reset ownership at the end.
- Keep access rules in one place. Scattered
.htaccessfiles in many folders are hard to review. Collect rules in the main configuration or one top-level file where possible, with a comment explaining each. - Review firewall changes. When you add a bot or country rule, test it immediately against a normal browser, Googlebot through URL Inspection, and your own monitoring tools.
- Watch for 403 spikes. A sudden rise in 403s in the access log, especially for search engine user agents, usually follows a plugin update or a new firewall rule. Catching it the same day avoids pages dropping out of the index.
- Audit after changes. Re-run the HTTP Error Audit after security changes, server moves and plugin updates, not only when something looks wrong.
1. Why do I get 403 Forbidden on my own website?
Something between you and the file has refused the request on purpose. On your own site the usual causes are files or folders the web server is not allowed to read, often after an upload as root, a deny rule in .htaccess or the server configuration that matches more than intended, a folder with no index file, or a firewall or security plugin rule. The response body and the error log line for that request tell you which one it is, so check them before changing anything.
2. What permissions should my website files have to avoid 403 errors?
On typical Linux hosting, folders at 755 and files at 644, owned by the site user, is a safe starting point. The web server also needs execute permission on every parent folder of a file, so check the whole path with namei -l, not just the file itself. Some managed hosts use tighter settings with a shared group, so follow their documentation when it differs. Do not use 777, which is unsafe and can cause other errors.
3. Is a 403 error bad for SEO?
Yes, if it affects pages you want in search. Google cannot index a page that returns 403, and pages that keep returning it are eventually dropped. A 403 on a private admin area is fine and expected. The real risk is a firewall or bot rule that refuses Googlebot or Bingbot across the site, which can look like the whole site has vanished. Check with URL Inspection in Search Console that Google can fetch your key pages.
4. Why does my page work in the browser but show 403 in the audit?
The audit is an automated client, and many firewalls, CDNs and security plugins treat automated requests differently from a person browsing. Bot protection, rate limits, user agent rules and country blocks can all refuse a crawler while letting your browser through. Compare the response for a browser user agent and a plain one with curl. If only automated requests fail, check the firewall or CDN event log for the rule that matched, then decide whether search crawlers are caught too.
5. What is the difference between 401 and 403?
A 401 means the server needs you to authenticate and is asking for credentials, as with a password-protected staging site. A 403 means the server knows what you asked for and refuses to serve it, and logging in will not change that. If a page should require a login, 401 or a redirect to a login page is more accurate. If a page should be public and returns 403, a rule or permission setting is wrong and needs correcting.
6. Should I switch on directory listing to fix a 403 on a folder?
No. The 403 appears because the folder has no index file and listing is turned off, which is the safe default. Turning listing on would show every file in that folder to anyone, including backups, exports and files you never meant to publish. Instead add an index page to the folder, redirect the folder address to the real page it should show, or fix the internal link that points at the bare folder address.
7. Can Cloudflare cause 403 errors on my site?
Yes. Cloudflare can return 403 from its own edge because of WAF managed rules, custom rules you wrote, Bot Fight Mode, country or IP access rules, or a hotlink setting. You can tell because the response has a cf-ray header and the request never appears in your server logs. Open the security events log in Cloudflare, search for that Ray ID, and adjust or add an exception to the specific rule rather than disabling protection for the whole zone.
The HTTP Error Audit requests every URL in your crawl scope, records the status code and response time, follows redirects and flags anything outside the 200 range, so pages refused with 403 show up alongside every other failure.
Open HTTP Error Fixes โ