{"record":{"id":"d4cd898eb5452ada","repo":"DIYgod/RSSHub","slug":"authentication-failed-access-denied-n-requestpa","errorCode":null,"errorMessage":"Authentication failed. Access denied.\\n${requestPath}","messagePattern":"Authentication failed\\. Access denied\\.\\\\n(.+?)","errorType":"exception","errorClass":"RejectError","httpStatus":403,"severity":"error","filePath":"lib/middleware/access-control.ts","lineNumber":8,"sourceCode":"import type { MiddlewareHandler } from 'hono';\n\nimport { config } from '@/config';\nimport RejectError from '@/errors/types/reject';\nimport md5 from '@/utils/md5';\n\nconst reject = (requestPath) => {\n    throw new RejectError(`Authentication failed. Access denied.\\n${requestPath}`);\n};\n\nconst middleware: MiddlewareHandler = async (ctx, next) => {\n    const requestPath = new URL(ctx.req.url).pathname;\n    const accessKey = ctx.req.query('key');\n    const accessCode = ctx.req.query('code');\n\n    if (['/', '/robots.txt', '/favicon.ico', '/logo.png'].includes(requestPath)) {\n        await next();\n    } else {\n        if (config.accessKey && !(config.accessKey === accessKey || accessCode === md5(requestPath + config.accessKey))) {\n            return reject(requestPath);\n        }\n        await next();\n    }\n};\n\nexport default middleware;","sourceCodeStart":1,"sourceCodeEnd":26,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/middleware/access-control.ts#L1-L26","documentation":"Thrown by the access-control middleware when an operator has set ACCESS_KEY and the incoming request fails both auth checks. A request is allowed only if query param `key` exactly equals config.accessKey, or query param `code` equals md5(requestPath + accessKey). Paths '/', '/robots.txt', '/favicon.ico', '/logo.png' are exempt. It uses RejectError, which RSSHub maps to a distinct rejection response (HTTP 403) rather than a generic 500.","triggerScenarios":"config.accessKey is set (e.g. ACCESS_KEY env) and the request to any non-exempt path omits both `?key=` and `?code=`, supplies a wrong key, or supplies a code computed against a different path/accessKey.","commonSituations":"Operator enables ACCESS_KEY to protect a public instance but forgets to distribute the key; client uses `?code=` computed with a stale or URL-encoded path; reverse proxy strips the query string; CI hits a protected route without credentials.","solutions":["Append `?key=<your ACCESS_KEY value>` to the RSSHub URL.","Or compute `?code=md5(pathname + ACCESS_KEY)` (lowercase hex) and append it.","If protection is unwanted, unset the ACCESS_KEY env var and restart the instance.","Ensure no reverse proxy/CDN strips query parameters before the request reaches RSSHub."],"exampleFix":"// before\nhttps://rsshub.example.com/bbc\n// after (share-the-key form)\nhttps://rsshub.example.com/bbc?key=YOUR_ACCESS_KEY\n// after (code form: md5('/bbc' + ACCESS_KEY))\nhttps://rsshub.example.com/bbc?code=<md5hex>","handlingStrategy":"validation","validationCode":"// Client side: compute the access code before the call.\nimport crypto from 'node:crypto';\nconst ACCESS_KEY = process.env.ACCESS_KEY!;\nconst pathname = new URL(targetUrl).pathname;\nconst code = crypto.createHash('md5').update(pathname + ACCESS_KEY).digest('hex');\nconst authedUrl = new URL(targetUrl);\nif (!authedUrl.searchParams.has('key') && !authedUrl.searchParams.has('code')) {\n  authedUrl.searchParams.set('code', code);\n}","typeGuard":"// Narrow an authenticated request before sending.\nconst isAuthed = (u: URL, accessKey: string): boolean =>\n  u.searchParams.get('key') === accessKey ||\n  u.searchParams.get('code') ===\n    crypto.createHash('md5').update(u.pathname + accessKey).digest('hex');","tryCatchPattern":"// Operators usually do not catch this — it is a deliberate 403.\n// If automating, treat a 403 from access-control as a credentials issue, not a retry candidate.\nif (res.status === 403 && /Access denied/.test(body)) {\n  throw new Error('RSSHub access key missing/wrong — fix credentials, do not retry');\n}","preventionTips":["Document the ACCESS_KEY and the md5(path+key) code scheme to all feed consumers.","Ensure reverse proxies preserve the query string.","Use the same ACCESS_KEY value across all clients; rotate by updating everyone in lockstep."],"tags":["auth","config","middleware","access-control"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}