Skip to content
← All articles

HTTP 403 Forbidden Error: 8 Ways to Fix

The 403 Forbidden error means the server understood the request but refuses to fulfill it. Unlike 401 (unauthenticated), 403 says: "I know who you are, but you have no access." It is a server-side access policy — on files, directories, or specific URLs.

We review 8 real causes of 403 and practical fixes for nginx, Apache, WordPress, and how to detect blocks by ModSecurity, Cloudflare WAF, or rate limiters.

What 403 Means

Per RFC 9110 §15.5.4, 403 indicates the server understands the request but refuses to authorize it. Re-authentication will not help — the decision is server-side (ACL, permissions, policy).

8 Common Causes

  1. Wrong file permissions — 600, 640 instead of 644.
  2. File owner is not the web server user (root:root instead of www-data:www-data).
  3. No index file in the directory and directory listing disabled.
  4. Block in .htaccess or nginx (deny all).
  5. ModSecurity or WAF rule blocked a suspicious request.
  6. IP block via Cloudflare, fail2ban, or firewall.
  7. SELinux/AppArmor denies web server access to the file.
  8. Hotlink protection — Referer does not match the allowlist.

Diagnosis

Always start with logs. 403 is a server code, and the reason is there.

# nginx
tail -50 /var/log/nginx/error.log | grep -i "forbidden\|403"

# Apache
tail -50 /var/log/apache2/error.log
tail -50 /var/log/apache2/access.log | grep " 403 "

# ModSecurity
tail -50 /var/log/modsec_audit.log

# Check permissions
ls -la /var/www/html/problem-file
# Expected: -rw-r--r-- 1 www-data www-data

Use the HTTP Header Checker for the external response and Security Scanner to check whether the server leaks information through headers.

8 Solutions

1. Fix file permissions

# Files
find /var/www/html -type f -exec chmod 644 {} \;
# Directories
find /var/www/html -type d -exec chmod 755 {} \;
# Owner
chown -R www-data:www-data /var/www/html

2. Verify index file

# nginx
index index.php index.html;

# Apache
DirectoryIndex index.php index.html

3. Find blocks in .htaccess

# Look for:
Deny from all
Require all denied

# Replace with:
Require all granted

4. nginx: deny directives

# Problematic:
location ~ \.(env|git|htaccess)$ {
    deny all;
}

# If too broad — review the pattern

5. ModSecurity false positive

# Identify the rule from the log:
# [id "949110"] [msg "Inbound Anomaly Score Exceeded"]

# Disable for a specific URL
SecRule REQUEST_URI "@beginsWith /api/safe-endpoint" \
    "id:1001,phase:1,ctl:ruleRemoveById=949110,nolog,allow"

6. Check IP blocks

# fail2ban
fail2ban-client status
fail2ban-client unban YOUR_IP

# iptables
iptables -L -n | grep DROP
iptables -D INPUT -s BLOCKED_IP -j DROP

7. SELinux context (CentOS/RHEL)

ls -Z /var/www/html/
# Should be httpd_sys_content_t

restorecon -Rv /var/www/html/
chcon -t httpd_sys_content_t /var/www/html/newfile.php

8. WordPress

403 on /admin or /wp-admin

If the site works but admin returns 403:

  1. Check IP allowlist in .htaccess or nginx.
  2. Review Cloudflare Firewall Rules on the admin path.
  3. Check fail2ban — your IP may be banned after failed logins.
  4. Verify 2FA — some plugins return 403 on wrong TOTP.

Frequently Asked Questions

Q: What is the difference between 401 and 403?
A: 401 = "authenticate" (no credentials). 403 = "you have no access" (credentials exist but insufficient rights).

Q: After scp the file returns 403, why?
A: The default owner is root. Run chown www-data:www-data file && chmod 644 file.

Q: How do I tell 403 from the server vs from a WAF?
A: Check the Server: and cf-ray headers. Cloudflare usually shows its own branded page with an ID. The Enterno.io HTTP Checker shows all headers.

Q: 403 for Googlebot — is that bad?
A: Critical. Google cannot index the page. Review robots.txt and WAF — make sure Googlebot (official IP list) is not blocked.

Conclusion

403 is a conversation about access rights. Start with logs, verify file permissions and ownership, hunt for deny rules in .htaccess and nginx, rule out WAF and IP blocks. Configure monitoring with expected_code=200 to catch 403 regressions instantly.

Check your website right now

Check now →
More articles: HTTP
HTTP
HTTP 301 vs 302 Redirect: Differences and When to Use Each
15.04.2026 · 5 views
HTTP
HTTP Status Codes: Complete Reference with Examples
10.03.2025 · 64 views
HTTP
X-Forwarded-For Header: Understanding Client IP Behind Proxies
16.03.2026 · 57 views
HTTP
HTTP 404 Not Found Error: 7 Causes and How to Fix
15.04.2026 · 9 views