Distribution guide

How to install an IPA over the air (OTA) — complete 2026 guide

Updated May 2026 · ~8 min read

What's in this guide

If you've ever shipped an iOS build to someone outside the App Store, you've hit the question: "How do I get this .ipa onto a real iPhone?"

The official answer is TestFlight. It works, but it has friction: testers need an Apple ID, an invitation email, the TestFlight app installed, and Apple has to approve each build first. For internal testing, ad-hoc beta distribution, or sharing a build with a client at 11pm before a demo, that's a lot of steps.

The shortcut is over-the-air (OTA) installation. It's been built into iOS since iOS 4. The user opens a link in Safari, taps Install, and the app downloads directly to their home screen — no App Store, no TestFlight. This guide walks through exactly how it works and how to set it up.

Why OTA install at all?

The catch: every device that installs your IPA must already be registered in the provisioning profile at the time you signed the build. Apple's anti-piracy mechanism. Add a tester after the fact and you need to re-sign and re-share.

What you need before you start

  1. A signed IPA. Either an ad-hoc build (signed against an ad-hoc provisioning profile with specific UDIDs listed) or an in-house enterprise build (signed against an Apple Developer Enterprise certificate). App Store builds don't work for OTA — they're locked to App Store distribution.
  2. The tester's UDID, registered in the profile. Apple checks this at install time. If their UDID isn't on the list, iOS refuses to launch the app (it'll install fine; it just won't open). Need a tester's UDID? Send them to our UDID lookup tool.
  3. HTTPS hosting. Both the manifest plist and the IPA file must be served over HTTPS. iOS refuses plain HTTP and self-signed certs since iOS 7.1. Let's Encrypt is free; CloudFront, S3, GitHub Pages, Vercel, and Netlify all work. Hosted on your own server? Make sure TLS is set up.
  4. A modern enough iOS. The mechanism has been around since iOS 4, but iOS 12.2+ tightened the install confirmation flow. The flow works on every iOS version anyone is currently running.

The manifest plist explained

The manifest is an XML property-list file that tells iOS three things: where the IPA is, what's inside it, and what to call it on the home screen. It looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>items</key>
  <array>
    <dict>
      <key>assets</key>
      <array>
        <dict>
          <key>kind</key>
          <string>software-package</string>
          <key>url</key>
          <string>https://your-host.com/path/to/YourApp.ipa</string>
        </dict>
        <dict>
          <key>kind</key>
          <string>display-image</string>
          <key>url</key>
          <string>https://your-host.com/path/to/icon-57.png</string>
        </dict>
        <dict>
          <key>kind</key>
          <string>full-size-image</string>
          <key>url</key>
          <string>https://your-host.com/path/to/icon-512.png</string>
        </dict>
      </array>
      <key>metadata</key>
      <dict>
        <key>bundle-identifier</key>
        <string>com.yourcompany.yourapp</string>
        <key>bundle-version</key>
        <string>1.2.0</string>
        <key>kind</key>
        <string>software</string>
        <key>title</key>
        <string>Your App Name</string>
      </dict>
    </dict>
  </array>
</dict>
</plist>

The three things iOS reads from this:

Save the plist with a .plist extension and serve it with Content-Type: application/xml (or omit the content-type; most servers default to a reasonable value).

The itms-services:// URL scheme

Once the manifest is hosted, you wrap its URL in iOS's special install URL scheme:

itms-services://?action=download-manifest&url=https://your-host.com/path/to/manifest.plist

That URL is what you put in the install button on your share page:

<a href="itms-services://?action=download-manifest&url=https://your-host.com/manifest.plist">
  Install on iPhone
</a>

When the tester taps it in Safari, iOS recognises the scheme, fetches the manifest, fetches the IPA, and offers to install. Open the same link in Chrome or Edge on iOS and it'll silently fail — only Safari is wired to handle itms-services://.

Where to host the IPA and manifest

You need somewhere HTTPS-accessible. The cheapest options for ad-hoc:

What the tester sees, step by step

  1. Tester opens your share link in Safari.
  2. Page renders with a "Install" button (which is just an <a href="itms-services://...">).
  3. Tester taps Install.
  4. iOS shows a confirmation: "This website is trying to install YourApp". Tester taps Install.
  5. The icon appears on the home screen with a progress ring. App downloads in the background.
  6. (First install only) Tester opens the app → iOS shows "Untrusted Developer". Tester goes to Settings → General → VPN & Device Management, taps the developer name, taps Trust. Subsequent installs from the same developer skip this step.

Troubleshooting

"Unable to download app" / "Could not be installed at this time"

App installs but won't launch ("This app cannot be installed because its integrity could not be verified")

"Untrusted Developer" on first launch

The link doesn't do anything in Chrome / Firefox / Edge on iOS

The easier way: skip all of this

If you're not building a hosting pipeline for the long haul, the manual approach above is more friction than it's worth for ad-hoc shares. App On The Go was built specifically to remove every step here:

Share an IPA in 30 seconds. Drop your file, get a QR code, send it to testers.
Upload an IPA →

Related reading