Security

CORB

Cross-Origin Read Blocking (CORB) and Opaque Response Blocking (ORB) - Browser security mechanisms that prevent malicious websites from reading sensitive cross-origin data, protecting against Spectre-class side-channel attacks.

What is CORB and ORB?

Cross-Origin Read Blocking (CORB), now evolved into Opaque Response Blocking (ORB), represents a critical browser security mechanism protecting against Spectre and other side-channel attacks. These features prevent malicious web pages from loading sensitive cross-origin resources into the renderer process where attackers could potentially extract data through timing attacks or CPU speculation vulnerabilities. CORB/ORB operates transparently, blocking dangerous cross-origin reads without breaking legitimate web functionality.

Unlike CORS which controls whether origins can make cross-origin requests at all, CORB/ORB provides an additional security layer determining whether cross-origin response bodies ever reach the web page’s JavaScript context. Even if CORS allows a cross-origin request, CORB/ORB may still block the response if it appears to contain sensitive data and lacks proper security signals. This defense-in-depth approach protects users even when server CORS configurations are overly permissive or when zero-day Spectre-class vulnerabilities emerge.

How CORB/ORB Works

CORB/ORB analyzes cross-origin responses before delivering them to web pages, examining Content-Type headers, response bodies, and request contexts. Resources loaded through certain HTML tags like <img>, <script>, <link>, <video>, and <audio> receive special scrutiny because these tags historically allowed cross-origin loading for legitimate purposes (images from CDNs, scripts from third parties). However, attackers abuse these tags attempting to load sensitive JSON or HTML responses hoping to leak information through side channels.

The algorithm first checks whether responses claim to be HTML, JSON, or XML through Content-Type headers. These MIME types typically contain sensitive data—user profiles, authentication tokens, private documents—making them dangerous to expose in renderer processes where Spectre-class attacks operate. If the Content-Type matches a sensitive pattern and the resource was requested via a suspect tag, CORB/ORB performs content sniffing on the first kilobyte of response body confirming the MIME type declaration accuracy.

When CORB/ORB determines a response contains sensitive data loaded inappropriately, it replaces the response body with an empty result before delivering to the web page. The HTTP status code and headers remain intact preventing broken functionality for scripts checking response metadata, but the actual sensitive data never enters the renderer process memory. This surgical intervention blocks attacks while maintaining compatibility with existing web applications that might inadvertently trigger CORB/ORB protections.

// Understanding CORB/ORB decision making
interface CORBAnalysis {
  requestContext: 'script' | 'img' | 'link' | 'fetch' | 'xhr';
  crossOrigin: boolean;
  contentType: string;
  corsEnabled: boolean;
  nosniff: boolean;
  decision: 'allow' | 'block';
  reason: string;
}

function analyzeForCORB(
  url: string,
  context: string,
  response: Response
): CORBAnalysis {
  const currentOrigin = new URL(window.location.href).origin;
  const resourceOrigin = new URL(url).origin;
  const crossOrigin = currentOrigin !== resourceOrigin;

  const contentType = response.headers.get('content-type') || '';
  const corsHeader = response.headers.get('access-control-allow-origin');
  const nosniff = response.headers.get('x-content-type-options') === 'nosniff';

  // Determine if this is a sensitive MIME type
  const sensitiveMimeTypes = [
    'text/html',
    'application/json',
    'text/json',
    'application/xml',
    'text/xml'
  ];

  const isSensitive = sensitiveMimeTypes.some(mime =>
    contentType.toLowerCase().includes(mime)
  );

  // CORB decision logic
  let decision: 'allow' | 'block' = 'allow';
  let reason = 'Safe context or not cross-origin';

  if (crossOrigin && isSensitive) {
    if (['script', 'img', 'link'].includes(context)) {
      if (!corsHeader || corsHeader === 'null') {
        decision = 'block';
        reason = 'Sensitive cross-origin resource without CORS in unsafe context';
      } else if (nosniff) {
        decision = 'block';
        reason = 'X-Content-Type-Options: nosniff prevents cross-origin read';
      }
    }
  }

  return {
    requestContext: context as any,
    crossOrigin,
    contentType,
    corsEnabled: !!corsHeader,
    nosniff,
    decision,
    reason
  };
}

// Examples
const analysis1 = analyzeForCORB(
  'https://api.other.com/user.json',
  'img',
  new Response(null, {
    headers: {
      'Content-Type': 'application/json'
      // No CORS header
    }
  })
);
// decision: 'block' - JSON loaded as image without CORS

const analysis2 = analyzeForCORB(
  'https://cdn.other.com/logo.png',
  'img',
  new Response(null, {
    headers: {
      'Content-Type': 'image/png'
    }
  })
);
// decision: 'allow' - Image MIME type appropriate for img tag

CORB vs CORS Differences

CORB and CORS serve complementary but distinct security purposes. CORS implements an opt-in permission system where servers explicitly allow specific origins to access their resources. Server administrators configure CORS headers declaring which origins may read responses, which HTTP methods are permitted, and whether credentials should be included. CORS operates at the network level, controlling whether requests complete successfully and whether JavaScript can access response data.

CORB operates after CORS, providing automatic protection even when CORS permissions are overly permissive. If a server mistakenly sets Access-Control-Allow-Origin: * on sensitive JSON endpoints, CORS would permit any origin to access the data. However, CORB still blocks that JSON from loading via <img> or <script> tags, preventing Spectre-class side-channel attacks regardless of CORS misconfigurations. This defense-in-depth ensures user protection even when server security policies have flaws.

Developers control CORS through server configuration but cannot directly control CORB/ORB—it operates automatically based on browser algorithms analyzing response characteristics. While CORS requires explicit headers allowing cross-origin access, CORB/ORB assumes cross-origin reads are dangerous unless specific safety signals appear. Servers can influence CORB/ORB behavior through proper Content-Type headers and X-Content-Type-Options: nosniff directives, but the final blocking decision remains with browser security algorithms.

// CORS and CORB interaction scenarios
interface SecurityScenario {
  name: string;
  serverConfig: {
    contentType: string;
    corsHeaders: Record<string, string>;
    nosniff: boolean;
  };
  loadMethod: string;
  corsResult: 'allow' | 'block';
  corbResult: 'allow' | 'block';
  finalOutcome: string;
}

const scenarios: SecurityScenario[] = [
  {
    name: 'Public API with proper CORS',
    serverConfig: {
      contentType: 'application/json',
      corsHeaders: {
        'Access-Control-Allow-Origin': 'https://trusted.com'
      },
      nosniff: true
    },
    loadMethod: 'fetch()',
    corsResult: 'allow',
    corbResult: 'allow',
    finalOutcome: 'Data accessible to JavaScript'
  },
  {
    name: 'Sensitive JSON loaded as image',
    serverConfig: {
      contentType: 'application/json',
      corsHeaders: {},
      nosniff: false
    },
    loadMethod: '<img src="">',
    corsResult: 'allow', // CORS not enforced for img
    corbResult: 'block',
    finalOutcome: 'Empty response, data protected from Spectre attacks'
  },
  {
    name: 'Misconfigured sensitive endpoint',
    serverConfig: {
      contentType: 'application/json',
      corsHeaders: {
        'Access-Control-Allow-Origin': '*'
      },
      nosniff: false
    },
    loadMethod: '<script src="">',
    corsResult: 'allow',
    corbResult: 'block',
    finalOutcome: 'CORB protects despite permissive CORS'
  },
  {
    name: 'CDN image with CORS',
    serverConfig: {
      contentType: 'image/png',
      corsHeaders: {
        'Access-Control-Allow-Origin': '*'
      },
      nosniff: false
    },
    loadMethod: '<img src="">',
    corsResult: 'allow',
    corbResult: 'allow',
    finalOutcome: 'Image loads normally'
  },
  {
    name: 'Private API without CORS',
    serverConfig: {
      contentType: 'application/json',
      corsHeaders: {},
      nosniff: true
    },
    loadMethod: 'fetch()',
    corsResult: 'block',
    corbResult: 'allow', // Never reached due to CORS
    finalOutcome: 'Request blocked by CORS before CORB evaluation'
  }
];

X-Content-Type-Options and nosniff

The X-Content-Type-Options: nosniff header plays a critical role in CORB/ORB protection by preventing browsers from MIME-type sniffing. Without this header, browsers may ignore Content-Type declarations and attempt to determine file types by examining content, a behavior that can be exploited for attacks. When nosniff is set, browsers strictly honor declared Content-Types rejecting resources that don’t match their declared types.

CORB/ORB uses nosniff as a strong signal that server administrators understand their content types and want strict enforcement. Resources served with nosniff receive more aggressive CORB/ORB protection because the header indicates servers expect browsers to respect Content-Type declarations absolutely. This makes CORB/ORB blocking decisions more confident and protects against attacks attempting to disguise sensitive data with incorrect MIME types.

Servers should set X-Content-Type-Options: nosniff on all responses, particularly those containing sensitive data. This header prevents MIME confusion attacks, strengthens CORB/ORB protections, and signals security awareness to browsers. Modern security scanners flag missing nosniff headers as vulnerabilities because the header provides important defense against content-type based attacks at minimal implementation cost.

// Implementing proper headers for CORB/ORB safety
class SecureResponseBuilder {
  buildJSONResponse(data: any, options: {
    publicAPI?: boolean;
    allowedOrigins?: string[];
  } = {}): Response {
    const headers = new Headers({
      'Content-Type': 'application/json',
      'X-Content-Type-Options': 'nosniff'
    });

    // Add CORS headers if this is a public API
    if (options.publicAPI) {
      headers.set('Access-Control-Allow-Origin', '*');
      headers.set('Access-Control-Allow-Methods', 'GET, POST');
    } else if (options.allowedOrigins && options.allowedOrigins.length > 0) {
      // Specific origin
      headers.set('Access-Control-Allow-Origin', options.allowedOrigins[0]);
      headers.set('Access-Control-Allow-Credentials', 'true');
    }

    return new Response(JSON.stringify(data), {
      headers,
      status: 200
    });
  }

  buildHTMLResponse(html: string, preventFraming: boolean = true): Response {
    const headers = new Headers({
      'Content-Type': 'text/html; charset=utf-8',
      'X-Content-Type-Options': 'nosniff'
    });

    // Prevent clickjacking
    if (preventFraming) {
      headers.set('X-Frame-Options', 'DENY');
    }

    return new Response(html, {
      headers,
      status: 200
    });
  }

  buildImageResponse(imageData: ArrayBuffer, mimeType: string): Response {
    const headers = new Headers({
      'Content-Type': mimeType,
      'X-Content-Type-Options': 'nosniff',
      'Access-Control-Allow-Origin': '*', // Images typically safe to share
      'Cache-Control': 'public, max-age=31536000'
    });

    return new Response(imageData, {
      headers,
      status: 200
    });
  }
}

// Usage
const builder = new SecureResponseBuilder();

// Public API endpoint
const publicResponse = builder.buildJSONResponse(
  { data: 'public information' },
  { publicAPI: true }
);

// Private API endpoint
const privateResponse = builder.buildJSONResponse(
  { userId: '123', email: 'user@example.com' },
  { allowedOrigins: ['https://app.example.com'] }
);

// HTML page
const htmlResponse = builder.buildHTMLResponse(
  '<!DOCTYPE html><html>...</html>',
  true
);

// Image from CDN
const imageResponse = builder.buildImageResponse(
  imageBuffer,
  'image/png'
);

Impact on Web Scraping and APIs

CORB/ORB primarily affects cross-origin resource loading in browsers, not server-to-server requests or API clients. Web scraping tools like cURL, Python requests, or Node.js fetch libraries don’t implement CORB/ORB because these protections specifically defend against browser-based side-channel attacks. Server-side scraping proceeds normally regardless of Content-Types or CORS headers since the security threat model differs fundamentally from browser contexts.

However, browser-based scraping—using headless browsers or browser automation tools—encounters CORB/ORB protections when attempting to load cross-origin resources through certain mechanisms. Scripts attempting to load JSON endpoints via <script> tags or other inappropriate contexts receive empty responses due to CORB/ORB blocking. Proper browser-based scraping uses fetch() or XMLHttpRequest which cooperate with CORS but aren’t subject to CORB/ORB blocking for normal request/response cycles.

APIs designed for public consumption should configure both CORS and proper Content-Types ensuring browser-based clients can access resources while CORB/ORB protections remain active for inappropriate access patterns. Private APIs not intended for browser access need only set correct Content-Types and nosniff headers allowing CORB/ORB to block any attempts to load them through browser exploitation techniques. This configuration provides security by default without requiring complex permission systems.

// Handling CORB/ORB in web scraping contexts
class ScrapingClient {
  async scrapewithBrowser(url: string, apiKey: string) {
    // Using CorsProxy with proper fetch() - not affected by CORB/ORB blocking
    const proxyUrl = `https://corsproxy.io/?url=${encodeURIComponent(url)}&key=${apiKey}&type=residential&colo=fra`;

    const response = await fetch(proxyUrl, {
      headers: {
        'Accept': 'application/json',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
      }
    });

    // Check if response might have been CORB-blocked
    const contentType = response.headers.get('content-type');
    const text = await response.text();

    if (text === '' && response.ok && contentType?.includes('json')) {
      console.warn('Empty JSON response - possible CORB block');
      return { blocked: true, reason: 'CORB/ORB protection' };
    }

    return { blocked: false, data: JSON.parse(text) };
  }

  async scrapeMultipleEndpoints(urls: string[], apiKey: string) {
    const results = await Promise.allSettled(
      urls.map(url => this.scrapewithBrowser(url, apiKey))
    );

    return results.map((result, index) => ({
      url: urls[index],
      status: result.status,
      data: result.status === 'fulfilled' ? result.value : null,
      error: result.status === 'rejected' ? result.reason : null
    }));
  }
}

// Server-side scraping (not affected by CORB/ORB)
async function serverSideScrape(url: string) {
  // Node.js, Python, cURL, etc. don't implement CORB/ORB
  const response = await fetch(url);
  const data = await response.json();
  return data; // Always works regardless of Content-Type
}

// Usage
const scraper = new ScrapingClient();

// Browser-based scraping through CorsProxy
const browserResult = await scraper.scrapewithBrowser(
  'https://api.example.com/data',
  'your-api-key'
);

// Multiple endpoints
const multipleResults = await scraper.scrapeMultipleEndpoints([
  'https://api.example.com/users',
  'https://api.example.com/posts',
  'https://api.example.com/comments'
], 'your-api-key');

Using CorsProxy with CORB/ORB

CorsProxy operates server-side where CORB/ORB protections don’t apply, then serves responses to browsers with appropriate CORS headers enabling legitimate cross-origin access. When applications fetch data through CorsProxy using fetch() or XMLHttpRequest, they avoid CORB/ORB blocking because these methods represent approved cross-origin communication channels. The browser’s CORS check passes due to CorsProxy headers, and CORB/ORB doesn’t interfere with standard fetch API usage.

To bypass both CORS and CORB/ORB restrictions, wrap your API URL with CorsProxy and use fetch():

// Fix CORB/ORB blocking with CorsProxy
// Original request that may be blocked by CORB/ORB:
const blockedUrl = 'https://api.example.com/data.json';

// Fixed using CorsProxy with fetch() API:
const proxyUrl = `https://corsproxy.io/?url=${encodeURIComponent('https://api.example.com/data.json')}`;

const response = await fetch(proxyUrl);
const data = await response.json();  // Works! No CORB/ORB blocking

// With authentication:
const authenticatedUrl = `https://corsproxy.io/?url=${encodeURIComponent('https://api.example.com/data.json')}&key=your-api-key`;

However, applications cannot bypass CORB/ORB by using CorsProxy with inappropriate loading methods. Attempting to load CorsProxy URLs via <img src="https://corsproxy.io/...json"> still triggers CORB/ORB evaluation on the final response. If CorsProxy returns JSON content with application/json Content-Type, CORB/ORB blocks that response from reaching the img tag handler regardless of where it originated. This ensures CORB/ORB protections remain effective even when requests pass through proxy services.

Proper CorsProxy usage for CORB/ORB compatibility requires using fetch() or XMLHttpRequest APIs rather than resource tags. These modern APIs work cooperatively with CORS and aren’t subject to CORB/ORB blocking for legitimate use cases. Applications migrating from tag-based resource loading to proper fetch APIs gain both CORB/ORB compatibility and cleaner security models with explicit CORS permission handling.

// CorsProxy usage avoiding CORB/ORB issues
class CORBSafeProxyClient {
  constructor(private apiKey: string) {}

  async fetchJSON(url: string): Promise<any> {
    const proxyUrl = `https://corsproxy.io/?url=${encodeURIComponent(url)}&key=${this.apiKey}&type=residential&colo=fra`;

    // Using fetch() - proper CORS channel, no CORB/ORB blocking
    const response = await fetch(proxyUrl, {
      headers: {
        'Accept': 'application/json'
      }
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    const contentType = response.headers.get('content-type');

    if (!contentType?.includes('json')) {
      console.warn('Unexpected content-type:', contentType);
    }

    return response.json();
  }

  async fetchHTML(url: string): Promise<string> {
    const proxyUrl = `https://corsproxy.io/?url=${encodeURIComponent(url)}&key=${this.apiKey}&type=residential&colo=fra`;

    const response = await fetch(proxyUrl, {
      headers: {
        'Accept': 'text/html'
      }
    });

    return response.text();
  }

  // DO NOT DO THIS - Will trigger CORB/ORB
  loadJSONAsImage(url: string): Promise<never> {
    return Promise.reject(
      new Error('Cannot load JSON as image - CORB/ORB will block this')
    );
  }

  // Proper pattern for images
  async loadImage(url: string): Promise<HTMLImageElement> {
    const proxyUrl = `https://corsproxy.io/?url=${encodeURIComponent(url)}&key=${this.apiKey}&type=residential&colo=fra`;

    return new Promise((resolve, reject) => {
      const img = new Image();
      img.crossOrigin = 'anonymous'; // Enable CORS
      img.onload = () => resolve(img);
      img.onerror = () => reject(new Error('Image load failed'));
      img.src = proxyUrl;
    });
  }
}

// Usage
const client = new CORBSafeProxyClient('your-api-key');

// Correct: JSON via fetch
const jsonData = await client.fetchJSON('https://api.example.com/data.json');

// Correct: HTML via fetch
const htmlContent = await client.fetchHTML('https://example.com/page.html');

// Correct: Image via img tag
const imgElement = await client.loadImage('https://example.com/photo.jpg');

Testing for CORB/ORB Protection

Developers should test APIs ensuring CORB/ORB protections activate appropriately for sensitive endpoints while allowing legitimate resource loading. Browser developer tools show CORB/ORB warnings when responses are blocked, appearing as console warnings indicating cross-origin reads were prevented. Automated testing can verify proper Content-Type headers, nosniff directives, and CORS configurations preventing both accidental CORB/ORB blocking of legitimate resources and security gaps allowing inappropriate access.

Testing sensitive endpoints involves attempting to load them via inappropriate contexts—JSON through <img> tags, HTML through <script> tags—verifying browsers block these attempts. Developer tools network panels show requests completing successfully (200 OK status) but with warnings that response bodies were blocked. This confirms CORB/ORB protection without breaking legitimate access through proper APIs.

Security audits should verify all endpoints serving sensitive data (JSON APIs, HTML pages with user information, XML documents) set appropriate Content-Types and include X-Content-Type-Options: nosniff. These headers ensure CORB/ORB protections activate consistently across different browsers and protect against zero-day Spectre-class vulnerabilities that might emerge. Regular scanning prevents configuration drift compromising security over time.

Best Practices and Recommendations

Always set accurate Content-Type headers matching response body formats. JSON should use application/json, HTML should use text/html, and images should use appropriate image/* types. Browsers rely on these headers for CORB/ORB decisions, and incorrect declarations can either block legitimate resources or fail to protect sensitive data. Automated testing should verify Content-Type correctness across all endpoints.

Include X-Content-Type-Options: nosniff on all responses, particularly those containing sensitive information. This header costs nothing to implement but significantly strengthens CORB/ORB protections and prevents MIME confusion attacks. Security scanners universally recommend nosniff, and its absence represents an easily fixed vulnerability.

Design APIs assuming CORB/ORB protection exists and will block inappropriate access patterns. Don’t rely on CORB/ORB as sole security—implement proper authentication, authorization, and CORS policies. However, recognize CORB/ORB provides valuable defense-in-depth catching misconfigurations and protecting against Spectre-class attacks that bypass traditional security boundaries.

Learn More

Create a free Account to fix CORB Errors in Production

Say goodbye to CORS errors and get back to building great web applications. It's free!

CORSPROXY Dashboard

Related Terms

More in Security

Related guides

Back to Glossary