惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
宝玉的分享
宝玉的分享
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
Vulnerabilities – Threatpost
博客园_首页
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
罗磊的独立博客
V
Visual Studio Blog
Know Your Adversary
Know Your Adversary
Hacker News - Newest:
Hacker News - Newest: "LLM"
美团技术团队
L
LINUX DO - 最新话题
The Last Watchdog
The Last Watchdog
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
云风的 BLOG
云风的 BLOG
N
Netflix TechBlog - Medium
MyScale Blog
MyScale Blog
The GitHub Blog
The GitHub Blog
有赞技术团队
有赞技术团队
I
InfoQ
Last Week in AI
Last Week in AI
V2EX - 技术
V2EX - 技术
量子位
S
Secure Thoughts
L
LangChain Blog
The Hacker News
The Hacker News
H
Help Net Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
小众软件
小众软件
K
Kaspersky official blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
I
Intezer
Vercel News
Vercel News
Hacker News: Ask HN
Hacker News: Ask HN
Cisco Talos Blog
Cisco Talos Blog
Google DeepMind News
Google DeepMind News
S
Securelist
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
Help Net Security
Help Net Security
Martin Fowler
Martin Fowler
爱范儿
爱范儿
Y
Y Combinator Blog
C
Check Point Blog

Giant Robots Smashing Into Other Giant Robots

Join us: Building Secure Healthcare Systems Upcase has retired, but the learning continues The Bike Shed Ep 506: The Muppet Software Team Migrating to native stack navigation, with a surprise from iOS 26 Past and present thoughtbotters at LRUG this Monday The Bike Shed Ep 505: What is a “principal” or “staff” engineer? Your vibe coded website is going to get you fined Roux’s New Component Library Why we're choosing stewardship over an exit The Bike Shed Ep 503: Seeing the Graph for the Trees Announcing Shoulda Matchers 8.0: validate multiple attributes in one line AI's "overnight" solution for our flaky tests took two weeks to adopt The Playwright debugging tool Rails devs aren't using Meet thoughtbot at Brighton Ruby 2026 614: AI Code Audits The mistake I didn't realise I was making when designing workshops AI crawlers are inflating your view counts 502: Apps That Make Our Work Go Toast: the 2-minute test that reveals how you think about building products Enforcing Your Ruby Style Guide on AI-Generated Code Copy as Markdown: AI-friendly blog posts 501: What makes for good technical writing? The Four Signals of AI Observability Can you really launch a tech business with a no-code app builder? 612: Do fish drink? This week in #dev (May 15, 2026) Lost, forgotten, and unfamiliar HTML 500: Celebrating with past hosts Why Duck Typer? Biometrics authentication for your mobile app
How I Built a Chrome Extension Wrapper (and Everything That Tried to Stop Me)
Valeria Graffeo · 2026-06-09 · via Giant Robots Smashing Into Other Giant Robots

So one day a client came to me and said: “we want a Chrome extension.” Simple enough, right?

First thing to know: the client didn’t want to build a real extension from scratch with its own codebase and logic. They wanted a wrapper, essentially their existing Rails app, dressed up and living inside a Chrome extension panel.

Same features, same views, same assets. Just… accessible from the browser bar.

That sounds simpler than building from scratch. But, was it?

Exploring options

Now, when it comes to Chrome extensions, there are a few main UI patterns:

  • Side panel: slides in like a drawer on the right side of the screen
  • Popup: the little window that appears when you click the extension icon in Chrome’s toolbar
  • Iframe overlay: inject an iframe into the current page, absolutely positioned wherever you want

The trigger can be a button injected into the page (via content_scripts, positioned with CSS), or the native Chrome extension icon in the browser bar. We explored a few options before eventually landing on a reference to model it after.

We ended up going with an iframe approach: a wrapper around the main Rails app, rendering its views and assets inside the extension panel.

The Stack Problem: Rails Didn’t Like This

Here’s where it gets fun. The code for the extension, the page with the trigger, the button, the iframe, all of it, lived inside the same Rails app that hosted the main product. Why? Because it needed to render views and serve assets from that app, and stay in sync if the main content changed.

But running two things from one Rails app (the main UI + the extension wrapper) is… not a great time. Locally, we ran into endless Rack-CORS errors. Trial and error sessions that felt like a personal war.

In hindsight, the much cleaner solution would have been to spin up a separate lightweight app just for the extension code, pointed at the main app as an API. Staying in one repo is a common and totally understandable constraint: less duplication, easier to keep things in sync. The tradeoff is exactly what we ran into: CORS complexity and two contexts fighting each other locally. Worth knowing before you commit to that architecture.

Auth: The Real Boss Fight

Okay, if CORS was annoying, auth was brutal.

The main app used Devise with cookie-based sessions. Totally normal for a Rails app only. Totally terrible when you’re trying to embed it inside a Chrome extension where there’s now another session in a different context.

Cookies + iframes + different origins = CHAOS. The sessions would conflict, auth state got confused between the main UI and the extension view, and debugging it felt like a nightmare.

The core issue: the extension iframe was loading the app from the same origin as the main app, so cookies were being shared, but in ways that weren’t predictable or clean. Having the extension live in a separate app/repo with its own auth flow would have made this much more manageable.

Assets: Because Why Not Add One More Thing

Once auth was (mostly) sorted, the assets decided to have a turn.

Icons and images weren’t rendering inside the iframe. The Rails asset pipeline had opinions about how assets were served, and those opinions didn’t align with being loaded in a sandboxed Chrome extension context. Some path adjustments and pipeline config tweaks later, it was working, but it was one of those things where you fix it and never want to look at it again.

The Chrome Web Store Setup

Here’s the practical stuff that I wish was better documented:

You need a Chrome Developer account: a Google account registered as a Chrome Extension developer.

The client owned this account, which made sense since they’d be the publisher. But the Chrome Extension developer account didn’t support collaborators. So deployments of staging versions had to go through the client’s access. That’s a workflow you want to sort out early, when you are a developer on a client project: nothing worse than needing to push a fix and waiting for someone else to log in or test changes for you. And make debugging an extra hard task.

Anyhow, in practice, this is the flow:

  1. Create a placeholder app in the Chrome Web Store to get a unique extension ID
  2. Hardcode that ID into your Rails config (initializers/environment config). It’s used to toggle the extension open and controls how the iframe is allowed to load
  3. Develop locally using sandbox/unpublished mode
  4. Rotate to the production extension package when publishing

What I’d Do Differently

  • Separate repo for the extension code from day one. Fewer CORS headaches, cleaner auth, easier to reason about.
  • Get a reference from the client early. When they finally showed me the competitor’s extension they wanted to replicate, I’d already gone down two other paths. Ask “is there an example you want to match?” in the first meeting.
  • Plan for auth early. If your main app uses cookie sessions, figure out the extension auth strategy before you write a line of extension code.
  • Test assets in the extension context early. Don’t assume the pipeline will just work cause it probably won’t.

TL;DR

Chrome extensions are powerful and actually not that hard in isolation. The complexity explodes when you’re wrapping an existing app with existing auth and trying to share code. Clean separation of concerns is your friend. CORS is your nemesis. And always ask the client if they have a reference they want to take inspiration from.

Building a Chrome extension and not sure where to start? Now we know exactly how to. Let’s chat.