Expert Guide Series

What Security Steps Should I Take When Building a PWA?

Progressive web apps have become a proper alternative to native mobile apps over the past few years, and honestly, its about time. They offer the reach of the web with many of the features users expect from apps they download from app stores—push notifications, offline functionality, home screen installation. But here's the thing—because PWAs sit somewhere between traditional websites and native apps, they face security challenges from both worlds. And that's where things get a bit tricky.

I've built plenty of PWAs for clients who want the benefits of app-like experiences without the hassle of maintaining separate iOS and Android codebases. The appeal is obvious; you build once and it works everywhere. But every single time I start a new PWA project, security is one of the first conversations we need to have. Why? Because a PWA has access to powerful features that regular websites don't—background sync, camera access, geolocation, local storage. If you don't secure these properly, you're essentially leaving the front door wide open.

The biggest mistake I see with PWA development is treating security as an afterthought rather than a foundation you build everything else on top of.

The mobile web has changed dramatically since I started building apps. Users expect their data to be protected, browsers have introduced stricter security requirements, and regulations like GDPR mean you can't just wing it anymore. Getting PWA security right isn't optional—it's the baseline for any app you want people to actually trust and use. In this guide, we'll walk through the practical security steps you need to take when building a PWA, from the absolute must-haves through to the more advanced protections that separate amateur projects from professional ones.

Why HTTPS Is Non-Negotiable for PWAs

Right, lets talk about HTTPS and why you absolutely cannot skip this step. I mean it—your PWA simply wont work without it. Well, technically it'll work on localhost during development, but the moment you push it to production without HTTPS? You're done. The browser will block most of your PWA features and you'll be left wondering why nothing works.

Service workers—the technology that makes PWAs actually progressive—only run on HTTPS connections. No exceptions. This isn't some annoying restriction the browser makers added for fun; its a security measure that protects your users from some genuinely nasty attacks. Think about it: service workers can intercept network requests, cache data, and basically sit between your user and the internet doing whatever they're programmed to do. If someone on an unsecured network could inject their own service worker into your site? Bloody hell, they could steal everything.

What HTTPS Actually Protects

When you serve your PWA over HTTPS, you're protecting against man-in-the-middle attacks where someone intercepts the data flowing between your user and your server. Without encryption, anyone on the same WiFi network (coffee shops, airports, hotels) could potentially see everything—login credentials, personal data, payment information, the lot. HTTPS encrypts all that traffic so even if someone intercepts it, they cant read it.

Getting HTTPS Set Up

The good news? Setting up HTTPS is easier now than its ever been. Here are your main options:

  • Use a hosting provider that includes free SSL certificates (most do these days)
  • Get a free certificate from Let's Encrypt—they've made this process dead simple
  • Use a CDN like Cloudflare which provides SSL as part of their service
  • Purchase a certificate from a traditional certificate authority if you need extended validation

Once you've got your certificate installed, make sure you redirect all HTTP traffic to HTTPS automatically. Users shouldnt even have the option to access your PWA insecurely—just force that redirect at the server level and be done with it.

Service Worker Security Risks You Need to Know

Right, so lets talk about service workers—because honestly, they're brilliant for making PWAs work offline and load faster, but they also introduce some security risks that'll keep you up if you don't handle them properly. I've seen developers get excited about the power of service workers without fully understanding the potential issues, and its not pretty when things go wrong.

Here's the thing about service workers; they sit between your web app and the network, intercepting every request your app makes. That's incredibly powerful. But it also means that if someone manages to inject a malicious service worker into your PWA, they basically control everything your users see and do. They could intercept login credentials, redirect users to phishing sites, or serve up completely fake content without your users knowing. Actually mad when you think about it.

The Main Threats

The biggest risk is service worker hijacking—where an attacker gets their own service worker registered on your domain. This can happen through XSS vulnerabilities or if your registration code isn't properly secured. Once they've got a malicious service worker running, it persists even after the user closes their browser, which is genuinely concerning because it can keep serving bad content until its manually removed.

Another issue I see often is developers caching sensitive data without thinking it through. Service workers cache resources to work offline, but if you're caching authentication tokens or personal information, you're creating a security problem. Cache poisoning is also real—attackers can potentially manipulate what gets stored in the cache, and then your app serves compromised content to users.

Always set a specific scope for your service workers and never register them at the root level unless absolutely necessary. The narrower the scope, the less damage a compromised worker can do across your entire site.

Protecting Your Implementation

First thing—validate everything in your service worker code. Don't trust any input, even if it seems to come from your own app. Use strong Content Security Policies (we'll cover this more later) to prevent unauthorised scripts from registering service workers. And please, regularly audit what your service workers are caching and how long items stay in the cache.

I always recommend implementing integrity checks for your service worker files. Use Subresource Integrity (SRI) hashes where possible, and monitor for unexpected changes to your worker scripts. Set up proper logging so you can detect unusual activity—like a service worker being registered from an unexpected location or caching patterns that don't match your design.

Protecting User Data and Privacy

Right, so here's where things get serious—and honestly, where a lot of PWA developers make mistakes that could've been avoided. User data protection isn't just about ticking boxes for compliance; its about building trust with the people using your app. Break that trust once and they're gone, probably forever.

First thing you need to understand is what data you're actually collecting. I mean, really sit down and map it out. Every form field, every cookie, every bit of information stored in localStorage or IndexedDB needs to be accounted for. Why? Because under GDPR and similar regulations, you cant just collect data because it might be useful later—you need a legitimate reason for every piece of information you're gathering. And here's the thing, users are way more savvy about this stuff than they used to be.

When storing data locally in your PWA (which you'll probably need to do for offline functionality), encryption is your friend. Don't store sensitive information in plain text in localStorage or IndexedDB. I've seen apps store credit card details, passwords, even medical information in plain text locally—its a bit mad really. Use proper encryption libraries and make sure you're following best practices for key management. Sure, it adds complexity, but the alternative is a data breach waiting to happen.

Also, be really careful about what data persists in your service worker cache. Just because something needs to be available offline doesn't mean it should be cached indefinitely. Implement proper cache expiration policies and make sure you're clearing out sensitive data when users log out or close sessions. The service worker cache can become a security liability if you're not managing it properly; I've seen cached API responses containing personal data that stuck around for months after a user stopped using the app.

Securing Your API Connections

Right, lets talk about API security—because this is where a lot of PWAs end up vulnerable. I mean, your app is only as secure as its weakest point, and if you're not protecting those API calls properly, you might as well leave the front door wide open.

First thing first: never, ever include API keys or secrets in your client-side code. I've seen this mistake too many times and its always a disaster waiting to happen. Your PWA code runs in the browser, which means anyone with basic developer tools can inspect it. Those keys need to live on your server, not in your JavaScript files. Use environment variables and server-side endpoints that authenticate requests before they reach your actual API.

Token-based authentication is your friend here. When a user logs in, generate a token on your server and send it back to the PWA—then include that token in the headers of subsequent API requests. Make sure those tokens expire after a reasonable time period (usually a few hours) and implement refresh token logic so users dont have to keep logging in. Oh, and store those tokens securely; sessionStorage or httpOnly cookies are much better than localStorage for sensitive data.

The best API security setup is one where even if your client-side code is completely exposed, attackers still cant do anything useful with what they find

Rate limiting is another thing people forget about. Without it, someone can hammer your API with thousands of requests and either run up your hosting costs or find vulnerabilities through brute force. Implement rate limiting on your backend—most cloud providers make this pretty straightforward these days. And validate everything on the server side, because client-side validation is basically just a suggestion that anyone can bypass.

Content Security Policy Setup

Right, so Content Security Policy—or CSP as most developers call it—is basically a set of rules you give to the browser that says "only load resources from these approved sources". Its like a bouncer at a club, checking that everything coming into your PWA is on the guest list. And honestly, if you're not using CSP on your PWA, you're leaving the door wide open for all sorts of nasty attacks.

The most common threat CSP protects against is cross-site scripting, or XSS. This is where an attacker manages to inject malicious JavaScript into your app, and before you know it they're stealing user data or redirecting people to dodgy sites. I've seen this happen to clients who thought their app was secure—it's not pretty, and its definitely preventable with proper CSP configuration.

Setting Up Your Basic CSP

You implement CSP through an HTTP header that your server sends with every response. Here's what you need to think about:

  • Start with a strict default policy that blocks everything, then whitelist only what you need
  • Specify exactly which domains can serve scripts, styles, images, and fonts to your PWA
  • Use 'self' to allow resources from your own domain—this is usually your baseline
  • Avoid using 'unsafe-inline' for scripts because it defeats the whole purpose of CSP; you can hash or nonce your inline scripts instead
  • Test your policy in report-only mode first so you can see what breaks before enforcing it

Common Mistakes I See

The biggest mistake? Setting up CSP and then immediately adding 'unsafe-eval' or 'unsafe-inline' because something broke during testing. Sure, it fixes the immediate problem, but you've just opened up security holes. Take the time to properly hash your inline scripts or move them to external files instead. Your users data is worth the extra effort.

Authentication and Session Management

Right, so you've got your PWA running on HTTPS and your service workers are locked down—brilliant stuff. But here's where things get a bit tricky; you need to think about how users actually log in and stay logged in to your app. Authentication is one of those areas where I've seen developers make some proper mistakes over the years, and its not always because they're careless—it's just that mobile and web authentication has its own quirks that aren't immediately obvious.

First thing: never, ever roll your own authentication system unless you absolutely have to. I mean it. Use established solutions like OAuth 2.0, OpenID Connect, or Auth0. These exist because authentication is genuinely hard to get right, and one small mistake can expose your entire user base. When you do implement authentication, make sure you're storing tokens securely—and by that I mean using httpOnly cookies for session tokens, not localStorage or sessionStorage where JavaScript can access them. That's just asking for trouble if someone manages to inject malicious code into your app.

Session Token Best Practices

Your session tokens need proper management, and this is where PWAs can get a bit complicated. You need to handle token expiry gracefully (nobody likes getting kicked out mid-task), implement refresh tokens so users aren't constantly logging back in, and make sure your tokens are revoked server-side when users log out. Don't just clear them from the client—that doesn't actually invalidate them.

Always implement automatic session timeout for inactive users; 15-30 minutes is standard for sensitive applications, though you can extend this for lower-risk apps. Just make sure users get a warning before they're logged out so they don't lose their work.

Multi-Factor Authentication

Look, MFA might seem like overkill for some apps, but its become pretty much expected for anything handling sensitive data. The good news? Its easier than ever to implement. Use standards like TOTP (those six-digit codes from authenticator apps) or WebAuthn for biometric authentication. Users actually appreciate the extra security once they're set up—it's just about making the setup process as painless as possible.

Here's what you need to check when setting up authentication:

  • Use secure, httpOnly cookies for storing session tokens
  • Implement token refresh mechanisms so sessions don't expire annoyingly
  • Set appropriate CORS policies so only your domains can authenticate
  • Add rate limiting to prevent brute-force login attempts
  • Store passwords using bcrypt or Argon2—never plain text or MD5
  • Implement account lockout after failed login attempts

One more thing—and this catches people out—your PWA can be installed and run offline, but authentication still needs a connection. Make sure your app handles this gracefully; show users a clear message if they try to log in without connectivity, and if they're already logged in with a valid token, let them access whatever cached content you've got available. The user experience should degrade gracefully, not just break completely.

Keeping Your PWA Updated and Patched

Right, so you've built a secure PWA—but here's where things get tricky. Security isn't something you do once and forget about. Its an ongoing process, and honestly, this is where I see a lot of teams drop the ball. New vulnerabilities get discovered all the time; dependencies you're using get patched; browsers change their security models. If you're not staying on top of updates, you're basically leaving the door open for attackers.

The good news? PWAs are actually easier to update than native apps. No App Store approval process to wait for. No forcing users to download a new version. You push an update to your server, and when users next open your PWA, they get the new version—well, sort of. Your service worker needs to be configured properly to handle updates, which means checking for new versions and prompting users to refresh when critical security patches are available. I usually set up a system that automatically checks for service worker updates every time the app starts, but doesn't force immediate updates unless its a security fix. Users hate being interrupted, but they hate being hacked even more.

Keep a close eye on your dependencies too. Run npm audit regularly (or yarn audit if that's your thing). Most vulnerabilities in modern web apps come from third-party libraries, not the code we write ourselves. Set up automated tools like Dependabot or Snyk to alert you when packages need updating. And actually update them—don't just dismiss the notifications because you're busy. I've seen PWAs get compromised because of a known vulnerability in a package that had a fix available for months. Its not worth the risk, really. Schedule regular maintenance windows, test your updates in staging first, and keep your PWA as current as possible. Your users data depends on it.

Look, building a secure PWA isn't rocket science, but it does require you to think about security at every stage—not just as an afterthought when everything else is done. I've seen too many projects where security gets bolted on at the end, and honestly? That's when things go wrong and you end up with vulnerabilities that could have been avoided.

Here's the thing—every layer we've talked about matters. HTTPS isn't optional; it's the foundation everything else sits on. Your service workers need proper scope management and cache validation. Your APIs need authentication tokens that actually expire. Your Content Security Policy needs to be strict enough to protect users but flexible enough to let your app function properly. And yeah, keeping everything patched and updated is boring work, but its the difference between a secure app and a security nightmare waiting to happen.

The good news? Once you've got these security measures in place, they mostly run themselves. Sure, you'll need to monitor things and stay on top of updates, but the heavy lifting happens during development. Building security into your workflow from day one means you're not scrambling to fix problems later when real users are depending on your app.

One last thing I always tell clients—security is never "finished". New vulnerabilities get discovered, attack methods evolve, and what was secure last year might not be secure enough today. But if you follow the practices we've covered here, you'll have a solid foundation that protects your users' data and keeps your PWA running safely. And that's really what this is all about, isn't it? Building something people can trust to use everyday without worrying about their information getting compromised.

Subscribe To Our Learning Centre