{"record":{"id":"d631e084dd1e6d01","repo":"remix-run/remix","slug":"invalid-origin-json-stringify-origin-trailing","errorCode":null,"errorMessage":"invalid origin ${JSON.stringify(origin)}: trailing slash is not allowed","messagePattern":"invalid origin (.+?): trailing slash is not allowed","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cop-middleware/src/lib/cop.ts","lineNumber":181,"sourceCode":"\nfunction getHeaderValue(headers: Headers, name: string): string | null {\n  let value = headers.get(name)\n  if (value == null) {\n    return null\n  }\n\n  let trimmedValue = value.trim()\n  return trimmedValue === '' ? null : trimmedValue\n}\n\nfunction validateTrustedOrigin(origin: string): string {\n  let trimmedOrigin = origin.trim()\n  if (trimmedOrigin === '') {\n    throw new Error('trusted origin must not be empty')\n  }\n\n  if (trimmedOrigin.endsWith('/')) {\n    throw new Error(`invalid origin ${JSON.stringify(origin)}: trailing slash is not allowed`)\n  }\n\n  let parsedOrigin = parseOrigin(trimmedOrigin)\n  if (parsedOrigin == null) {\n    throw new Error(`invalid origin ${JSON.stringify(origin)}`)\n  }\n\n  if (parsedOrigin.pathname !== '/' || parsedOrigin.search !== '' || parsedOrigin.hash !== '') {\n    throw new Error(\n      `invalid origin ${JSON.stringify(origin)}: path, query, and fragment are not allowed`,\n    )\n  }\n\n  return serializeOrigin(parsedOrigin)\n}\n\nfunction normalizeOrigin(origin: string): string | null {\n  let parsedOrigin = parseOrigin(origin)","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/cop-middleware/src/lib/cop.ts#L163-L199","documentation":"Origins are compared as scheme://host strings, so a trailing '/' is rejected — 'https://x.com/' would never equal the serialized request Origin header and would silently fail to match.","triggerScenarios":"addTrustedOrigin('https://api.example.com/') — any origin string ending with '/'.","commonSituations":"Copy-pasting a site URL (which includes the slash) from a browser bar into config; building origins by concatenating a base URL variable that ends in '/'.","solutions":["Remove the trailing slash: 'https://api.example.com'","Strip it programmatically: `origin.replace(/\\/+$/, '')` before adding","Use a dedicated origin constant rather than a base-url variable"],"exampleFix":"// before\ncop.addTrustedOrigin('https://api.example.com/')\n// after\ncop.addTrustedOrigin('https://api.example.com')","handlingStrategy":"validation","validationCode":"cop.addTrustedOrigin(origin.replace(/\\/+$/, ''))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store origins as scheme://host constants","Never paste browser-bar URLs into config","Add a startup assertion that origins don't end in '/'"],"tags":["cop-middleware","security","origin","url-parsing"],"backgroundTag":"invalid-origin-config","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}