wpscanteam/wpscan · error · WPScan::Error::AccessForbidden

The target is responding with a 403, this might be due to a

Error message

The target is responding with a 403, this might be due to a WAF. Well... --random-user-agent didn't work, use --force to skip this check if needed.

What it means

Raised by Core#check_target_availability (app/controllers/core.rb:77) when the target answers the initial GET with HTTP 403 and --force is not set. This variant is constructed with WPScan::ParsedCli.random_user_agent == true, i.e. the scan already ran with --random-user-agent and still received 403, so the message states the random UA did not help and points at --force. It almost always indicates a WAF or server-level rule (Cloudflare, ModSecurity, IP allowlist) blocking the scanner for reasons beyond the User-Agent.

Source

Thrown at app/controllers/core.rb:77

        output('help', help: option_parser.full_help, simple: false) if WPScan::ParsedCli.hh
        output('version') if WPScan::ParsedCli.version

        exit(WPScan::ExitCode::OK) if WPScan::ParsedCli.help || WPScan::ParsedCli.hh || WPScan::ParsedCli.version
      end

      # Checks that the target is accessible, raises related errors otherwise.
      #
      # @return [ Void ]
      def check_target_availability
        res = WPScan::Browser.get(target.url)

        case res.code
        when 0
          raise Error::TargetDown, res
        when 401
          raise Error::HTTPAuthRequired
        when 403
          raise Error::AccessForbidden, WPScan::ParsedCli.random_user_agent unless WPScan::ParsedCli.force
        when 407
          raise Error::ProxyAuthRequired
        end

        handle_redirection(res)
      end

      # Checks whether the response or its redirect chain contains a SAMLRequest,
      # indicating that the target requires SAML authentication.
      #
      # @param [ Addressable::URI ] effective_uri  Final URL after following redirects
      # @param [ Typhoeus::Response ] homepage_res Response whose redirect chain to inspect
      #
      # @return [ Boolean ]
      def saml_request?(effective_uri, homepage_res = nil)
        return false unless effective_uri

        return true if effective_uri.to_s.match?(/[?&]SAMLRequest/i)

View on GitHub (pinned to 62c9cef471)

Solutions

  1. Re-run with --force to skip the 403 check if you accept scanning a site that is blocking you
  2. Get your scanning IP allowlisted by the site owner, or scan from an allowed network
  3. Route the scan through a different egress IP via --proxy that is not blocked
  4. If you administer the site, temporarily relax the WAF/IP rule for your IP during the scan

Example fix

# before
wpscan --url http://target --random-user-agent
# => ...403 ... --random-user-agent didn't work, use --force to skip this check if needed.

# after
wpscan --url http://target --force
Defensive patterns

Strategy: fallback

Validate before calling

# Detect the 403 block before running the scan
res = WPScan::Browser.get('http://target')
raise 'blocked (403): use --force or an allowed IP' if res.code == 403

Try / catch

begin
  scan.run
rescue WPScan::Error::AccessForbidden => e
  retry_with(force: true) if e.random_user_agent_used # conscious fallback: skip the 403 check
end

Prevention

When it happens

Trigger: `wpscan --url http://target --random-user-agent` (or random_user_agent: true in config) where WPScan::Browser.get(target.url) returns code 403: WAF rule matching scanner TLS/HTTP fingerprint, IP-based deny or allowlist, country block, or a global access rule — and no --force on the command line.

Common situations: Cloudflare/Sucuri in front of the site; ModSecurity CRS rules; nginx/apache allow-deny rules blocking the scanner IP; hosting provider blocking datacenter IP ranges; scanning through a VPN whose egress IP is blacklisted.


AI-assisted analysis of wpscanteam/wpscan@62c9cef471 (2026-08-21). Data as JSON: /api/errors/0f2a4f728f2dc49a. Report an issue: GitHub.