proxy Permission — Chrome Extension Reference

9 min read

proxy Permission — Chrome Extension Reference

Overview

manifest.json

{ "permissions": ["proxy"] }

Key APIs

chrome.proxy.settings (ChromeSetting pattern: get/set/clear)

Setting a Proxy

chrome.proxy.settings.set({
  value: {
    mode: "fixed_servers",
    rules: {
      singleProxy: { scheme: "http", host: "proxy.example.com", port: 8080 },
      bypassList: ["localhost", "127.0.0.1", "*.local"]
    }
  },
  scope: "regular"
});

Proxy Modes

PAC Script Mode

Getting/Clearing Settings

Error Handling

Proxy Rules Structure

Common Patterns

VPN-Like Extension

Per-Site Proxy

Proxy Toggle

Security Considerations

Using with @theluckystrike/webext-permissions

import { checkPermission, requestPermission, PERMISSION_DESCRIPTIONS } from "@theluckystrike/webext-permissions";

const result = await checkPermission("proxy");
console.log(result.description); // "Configure proxy settings"
console.log(result.granted);

PERMISSION_DESCRIPTIONS.proxy; // "Configure proxy settings"

// Proxy is sensitive — consider optional_permissions
if (!result.granted) {
  const req = await requestPermission("proxy");
  if (!req.granted) {
    showMessage("Proxy permission is required for this feature");
    return;
  }
}

Using with @theluckystrike/webext-messaging

Pattern: popup provides proxy controls, background applies settings:

type Messages = {
  setProxy: {
    request: { host: string; port: number; scheme?: string; bypassList?: string[] };
    response: { success: boolean };
  };
  clearProxy: {
    request: void;
    response: { success: boolean };
  };
  getProxyStatus: {
    request: void;
    response: { mode: string; host?: string; port?: number; controlledBy: string };
  };
};

// background.ts
import { createMessenger } from "@theluckystrike/webext-messaging";
const msg = createMessenger<Messages>();

msg.onMessage({
  setProxy: async ({ host, port, scheme, bypassList }) => {
    await chrome.proxy.settings.set({
      value: {
        mode: "fixed_servers",
        rules: {
          singleProxy: { scheme: scheme || "http", host, port },
          bypassList: bypassList || ["localhost", "127.0.0.1"],
        },
      },
      scope: "regular",
    });
    return { success: true };
  },
  clearProxy: async () => {
    await chrome.proxy.settings.clear({ scope: "regular" });
    return { success: true };
  },
  getProxyStatus: async () => {
    const config = await chrome.proxy.settings.get({ incognito: false });
    return {
      mode: config.value.mode,
      host: config.value.rules?.singleProxy?.host,
      port: config.value.rules?.singleProxy?.port,
      controlledBy: config.levelOfControl,
    };
  },
});

Using with @theluckystrike/webext-storage

Store proxy profiles and preferences:

import { defineSchema, createStorage } from "@theluckystrike/webext-storage";

const schema = defineSchema({
  proxyEnabled: false,
  activeProfile: "",
  proxyProfiles: [] as Array<{
    name: string;
    host: string;
    port: number;
    scheme: string;
    bypassList: string[];
  }>,
  autoSwitchRules: [] as Array<{ domain: string; profileName: string }>,
});
const storage = createStorage({ schema });

// React to proxy toggle changes from popup
storage.watch("proxyEnabled", async (enabled) => {
  if (enabled) {
    const profileName = await storage.get("activeProfile");
    const profiles = await storage.get("proxyProfiles");
    const profile = profiles.find(p => p.name === profileName);
    if (profile) {
      await chrome.proxy.settings.set({
        value: {
          mode: "fixed_servers",
          rules: {
            singleProxy: { scheme: profile.scheme, host: profile.host, port: profile.port },
            bypassList: profile.bypassList,
          },
        },
        scope: "regular",
      });
    }
  } else {
    await chrome.proxy.settings.clear({ scope: "regular" });
  }
});

Practical Example: PAC Script for Per-Site Routing

// Route specific domains through a proxy, everything else direct
function createPacScript(proxyHost: string, proxyPort: number, domains: string[]): string {
  const conditions = domains.map(d => `shExpMatch(host, "${d}")`).join(" || ");
  return `
    function FindProxyForURL(url, host) {
      if (${conditions}) {
        return "PROXY ${proxyHost}:${proxyPort}";
      }
      return "DIRECT";
    }
  `;
}

async function applyPerSiteProxy(host: string, port: number, domains: string[]) {
  const pac = createPacScript(host, port, domains);
  await chrome.proxy.settings.set({
    value: {
      mode: "pac_script",
      pacScript: { data: pac },
    },
    scope: "regular",
  });
}

Practical Example: Proxy Error Monitoring

chrome.proxy.onProxyError.addListener((details) => {
  console.error(`Proxy error: ${details.error} (fatal: ${details.fatal})`);

  if (details.fatal) {
    // Fatal error — fall back to direct connection
    chrome.proxy.settings.clear({ scope: "regular" });
    chrome.notifications.create({
      type: "basic",
      iconUrl: "icons/icon-128.png",
      title: "Proxy Connection Failed",
      message: "Falling back to direct connection. Check your proxy settings.",
    });
  }
});

Gotchas

Common Errors

Frequently Asked Questions

How do I configure proxy in Chrome extension?

Use chrome.proxy.settings.set() to configure proxy settings. You can set pac scripts, fixed servers, or use system settings.

Can extensions bypass the proxy for specific domains?

Yes, specify bypassList in your proxy configuration to exclude specific domains from proxy routing. —

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.

No previous article
No next article