Skip to main content
Blog|
How-to guides

HTTP 403 Forbidden: what it means and how to fix it

|
Apr 10, 2026|11 min read
HOW-TO GUIDESHTTP 403 Forbidden: what itmeans and how to fix itHOSTNEYhostney.comApril 10, 2026

A 403 Forbidden means the server understood your request and refused to fulfill it. It is not a credentials problem – that would be a 401 Unauthorized. With a 403, the server knows who you are (or does not care) and is deliberately denying access. The resource exists, the URL is correct, but something in the server’s configuration says you are not allowed to reach it.

This is one of the most common HTTP errors and one of the most frustrating because the response rarely tells you why access was denied. The fix depends entirely on which layer generated the 403 and what rule triggered it.

Quick reference#

SymptomLikely causeFix
403 on a newly deployed or migrated siteFile permissions too restrictive chmod 755 dirs, chmod 644 files
403 on a URL ending in / with no file specifiedDirectory listing disabledAdd an index file or request a specific file
403 on Apache, rules in .htaccess .htaccess deny ruleRemove or adjust the deny rule
403 with Nginx error log showing “Permission denied”File permissions or Nginx deny directiveFix ownership/permissions or adjust allow / deny
403 with Cloudflare branding or Ray ID in responseCloudflare firewall rule or bot protectionCheck Security Events in Cloudflare dashboard
403 after triggering a security rule (SQL/XSS pattern)WAF (ModSecurity) blocking the requestCheck audit log, disable the specific false-positive rule
403 from one network but not anotherIP-based ban (Fail2ban, firewall, blocklist)Unban the IP or add a firewall exception
403 from Google API with “does not have permission”Missing IAM role or API scopeGrant the required permission in Google Cloud console
403 in WordPress admin or on WP pagesWordPress-specific issueSee WordPress 403 guide

403 vs 401 - the difference matters#

These two errors get confused constantly, and the distinction is important for diagnosing the problem.

A 401 Unauthorized means authentication failed. Either no credentials were sent or the credentials were rejected. The server is saying “I do not know who you are – prove your identity.” Sending valid credentials fixes a 401.

A 403 Forbidden means authorization failed. The server may know exactly who you are and still refuse. Sending credentials again will not help. The server is saying “I know who you are, and the answer is no.”

In practice:

  • Getting a login prompt or “authentication required” message = 401 territory
  • Getting “access denied” or “forbidden” with no prompt = 403 territory
  • A 401 invites you to try again with credentials. A 403 is a final refusal.

Some servers return 403 instead of 401 to avoid revealing that a resource exists. If a file is behind authentication and the server returns 401, an attacker knows the file is there. Returning 403 (or even 404) hides that information.

What the HTTP specification says#

HTTP 403 is defined in RFC 9110, Section 15.5.4. The specification says the server understood the request but refuses to fulfill it. The server may explain the reason in the response body, but it is not required to. Re-authenticating will not help – the refusal is tied to the request itself, not to the credentials.

HTTP/1.1 403 Forbidden
Content-Type: text/html
Server: nginx

The specification explicitly notes that a 403 is not about authentication. If credentials could fix the problem, the correct response would be 401.

Common causes#

File permissions

On Linux servers, every file and directory has permissions that control who can read, write, and execute it. The web server process (usually running as www-data , nginx , apache , or a per-user account) needs read access to serve a file. If the file permissions are too restrictive, the server returns 403.

This is the most common cause on newly deployed or migrated sites. Typical correct permissions:

# Directories need execute permission to be traversable
chmod 755 /var/www/html
chmod 755 /var/www/html/subdir

# Files need read permission for the web server
chmod 644 /var/www/html/index.html
chmod 644 /var/www/html/style.css

If a file is owned by root and the web server runs as www-data , the “other” permission bits must allow reading. If a file has 600 permissions (owner read/write only), the web server cannot read it and returns 403.

Check permissions with:

ls -la /path/to/file

Look at both the file and every directory in the path. A 403 can come from a restricted parent directory even if the file itself has correct permissions.

Directory listing disabled

When a URL points to a directory (like https://example.com/images/ ) and no index file exists in that directory, the server has two options: show a directory listing or return 403.

Most production servers disable directory listings for security:

# Nginx
autoindex off;  # This is the default

# Apache
Options -Indexes

If you are getting a 403 on a URL that ends with / and no specific file, this is likely the cause. The fix is either to add an index file or to request a specific file within that directory.

.htaccess deny rules

On Apache servers, .htaccess files can deny access to specific paths, IPs, or request patterns:

# Block all access to a directory
Deny from all

# Block specific IPs
Order Allow,Deny
Allow from all
Deny from 192.168.1.100

# Apache 2.4+ syntax
Require all denied
Require ip 10.0.0.0/8

If you are getting 403 on an Apache server, check every .htaccess file in the path from the document root to the requested file. A .htaccess in a parent directory applies to all subdirectories.

Search for deny rules:

find /var/www -name ".htaccess" -exec grep -l -i "deny\|require" {} \;

Nginx access restrictions

Nginx uses deny and allow directives to control access:

location /admin/ {
    allow 10.0.0.0/8;
    deny all;
}

location ~ /\.ht {
    deny all;
}

These are commonly used to protect sensitive paths like admin panels, configuration files, and hidden directories. If your IP is not in the allow list, you get a 403.

Check the Nginx configuration for the site:

nginx -T | grep -A5 "deny\|allow"

Nginx also returns 403 when it cannot read a file due to permissions, identical to the file permissions issue described above. The error log will say “open() failed (13: Permission denied).”

ModSecurity or WAF blocking

Web application firewalls inspect every request for malicious patterns. If a request URL, header, cookie, or body matches a rule, the WAF returns 403 before the request reaches the application.

Common triggers:

  • SQL-like patterns in query strings ( ?id=1 OR 1=1 )
  • Script tags or JavaScript in form submissions (XSS patterns)
  • Path traversal patterns ( ../../etc/passwd )
  • Unusual user agents or missing headers
  • Requests to known exploit paths

ModSecurity logs blocked requests. Check the audit log:

tail -50 /var/log/modsec_audit.log

Each entry includes the rule ID that triggered the block. If the rule is a false positive, you can disable it specifically:

SecRuleRemoveById 942100

Do not disable the entire WAF to fix a single 403. Identify the specific rule and either disable that rule, adjust the request that triggers it, or whitelist the specific path.

Cloudflare or CDN blocking

If your site is behind Cloudflare or another CDN, the 403 might come from the CDN rather than your origin server. Cloudflare can block requests based on:

  • Firewall rules (IP-based, country-based, ASN-based)
  • Bot Fight Mode challenges that fail
  • Security level settings blocking low-reputation IPs
  • Hotlink protection blocking requests without a valid referer

If the 403 response page has Cloudflare branding or a Ray ID, the block is at the Cloudflare level, not your server.

Check Cloudflare’s Security Events in the dashboard to see what rule triggered the block. The event log shows the matching rule, the blocked IP, and the request details.

IP-based blocking

Multiple layers can block by IP address:

  • Fail2ban banning IPs after repeated failed logins
  • Server-level firewall rules (iptables, firewalld, nftables)
  • Application-level IP blocklists
  • Hosting infrastructure bot detection flagging suspicious traffic

If you can access the site from one network but not another, IP-based blocking is the likely cause. Try accessing from a different network or device to confirm.

Google “your client does not have permission”

A 403 from Google services (APIs, Cloud Storage, OAuth endpoints) with the message “your client does not have permission to get URL” has specific causes:

  • The Google Cloud IAM role does not include the required permission
  • The service account key is valid but lacks the scope for the requested resource
  • The API has not been enabled in the Google Cloud project
  • The OAuth consent screen is in testing mode and the requesting user is not listed as a test user

This is not a server configuration issue – it is Google’s authorization system rejecting the request at the API level.

How to diagnose which cause applies#

Step 1 – Check the response headers

The Server header tells you which layer generated the 403:

curl -I https://example.com/blocked-page
Server headerThe 403 came from
nginx or openresty Your Nginx/OpenResty web server
Apache Your Apache web server
cloudflare Cloudflare CDN
AkamaiGHost Akamai CDN
No Server headerApplication-level (WordPress, Node.js, etc.)

Step 2 – Check the error log

The web server’s error log usually explains why it returned 403:

# Nginx
tail -20 /var/log/nginx/error.log

# Apache
tail -20 /var/log/httpd/error_log

Common messages:

  • directory index of "/path/" is forbidden = directory listing disabled
  • open() "/path/file" failed (13: Permission denied) = file permissions
  • access forbidden by rule = Nginx deny directive
  • client denied by server configuration = Apache deny rule

Step 3 – Check if it is IP-specific

Access the same URL from a different network (mobile data, VPN, different device). If it works from another IP, the block is IP-based.

Step 4 – Check if it is path-specific

Try accessing the site root versus the blocked path. If the root works but a specific path returns 403, the block is path-specific (directory permissions, .htaccess rules, WAF rules).

Step 5 – Bypass the CDN

If the site is behind a CDN, curl directly to the origin server to determine whether the 403 comes from the CDN or the origin:

curl -I https://origin-ip/blocked-page -H "Host: example.com" -k

If the origin returns 200 but the CDN returns 403, the block is at the CDN level.

Fixing a 403#

Fix file permissions

# Set standard permissions for a web directory
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;

# Make sure the web server user owns the files
chown -R www-data:www-data /var/www/html

Adjust the username ( www-data , nginx , apache , or your hosting account name) to match your server’s configuration. On shared hosting, the web server may run as your user account rather than a system user.

Fix .htaccess rules

If a .htaccess file contains Deny from all or Require all denied , either remove the rule or add an exception for your IP:

# Allow your IP while blocking everyone else
Order Deny,Allow
Deny from all
Allow from 203.0.113.50

# Apache 2.4+
Require ip 203.0.113.50

If you do not recognize the .htaccess rules, a WordPress security plugin may have added them. Check Wordfence, Sucuri, or iThemes Security settings. For WordPress-specific 403 issues, see the WordPress 403 troubleshooting guide.

Fix Nginx deny rules

Edit the Nginx configuration for the site and either remove the deny all directive or add your IP to the allow list:

location /admin/ {
    allow 203.0.113.50;
    allow 10.0.0.0/8;
    deny all;
}

After editing, test and reload:

nginx -t && systemctl reload nginx

Fix WAF false positives

If ModSecurity or another WAF is blocking legitimate requests, identify the rule from the audit log and either:

  1. Disable the specific rule for the affected path
  2. Whitelist the specific request pattern
  3. Adjust the request to avoid triggering the rule

Never disable the entire WAF. Target the specific rule causing the false positive.

Fix CDN blocking

For Cloudflare:

  1. Go to Security > Events to find the blocked request
  2. Note the rule that triggered the block
  3. Either adjust the rule or create an exception (IP Access Rule, WAF exception)

For other CDNs, check the CDN’s security event log for the matching rule.

Fix IP-based bans

If Fail2ban banned your IP:

# Check if your IP is banned
fail2ban-client status
fail2ban-client status nginx-http-auth

# Unban your IP
fail2ban-client set nginx-http-auth unbanip 203.0.113.50

If a server-level firewall is blocking:

# Check iptables
iptables -L -n | grep 203.0.113.50

# Check firewalld
firewall-cmd --list-all

403 in APIs#

REST APIs return 403 when the authenticated user does not have the required role or permission for the requested action. This is different from a web server 403 – the application itself is enforcing access control.

Common API 403 scenarios:

  • An API key is valid but lacks the required scope (read-only key trying to write)
  • The authenticated user does not have the role required for the endpoint (viewer trying to delete)
  • CORS preflight request is rejected (the API does not allow the requesting origin)
  • Rate limiting with a 403 instead of the more standard 429 Too Many Requests

The API response body usually contains details about which permission is missing:

{
  "error": "forbidden",
  "message": "User does not have permission: admin.write"
}

Fix API 403 errors by checking the API documentation for required permissions and ensuring the API key or token has the correct scopes.

403 vs other errors#

Status codeWhat happened
401 UnauthorizedNo credentials or invalid credentials – authenticate first
403 ForbiddenCredentials accepted (or not required) but access denied
404 Not FoundThe resource does not exist at that URL
422 Unprocessable EntityRequest is valid but the data fails validation
500 Internal Server ErrorThe server crashed while processing the request

Summary#

A 403 Forbidden is an authorization failure, not an authentication failure. The server understood what you asked for and refused. The fix depends on which layer generated the 403: file permissions, web server deny rules, .htaccess rules, WAF rules, CDN firewall, IP bans, or application-level access control.

Start by identifying the source from the response headers and error logs, then work through the specific fix for that layer. For WordPress-specific 403 errors, see WordPress 403 forbidden error: how to fix it.

Related articles