Exchange ActiveSync (EAS) is a protocol for synchronizing email, contacts, calendar, tasks, and notes between mobile clients and a groupware server. EAS uses WAP Binary XML (WBXML) over HTTP/HTTPS. It was originally developed by Microsoft for Exchange, but is now supported by iOS Mail, Android, Outlook 2013+, Windows Mail, and many third-party clients.
In addition to synchronization, the protocol provides device management and security features (provisioning, remote wipe, PIN policies).
Michael Rubinsky
Torben Dannhauer
See the bug tracker, the list of known issues, and broken client behavior.
The Horde_ActiveSync library provides the protocol engine for synchronizing a Horde groupware stack with EAS clients. In a typical Horde 6 deployment the data backend is Horde_Core_ActiveSync_Driver in horde/core, which talks to IMP (mail), Kronolith (calendar), Turba (contacts), Nag (tasks), and Mnemo (notes).
For the feature matrix by EAS version, see ActiveSync Feature Grid. For developer-oriented protocol documentation, see the horde/activesync README and the API documentation.
Horde 6 ActiveSync passes Microsoft's Remote Connectivity Analyzer when provisioning is disabled for the test account (the analyzer does not respond to the HTTP 449 header sent when provisioning is required).
Activate ActiveSync in Horde administration -> ActiveSync tab (or var/config/horde/conf.php). Create the SQL state tables from the Horde configuration screen when prompted.
Minimum configuration:
$conf['activesync']['enabled'] = true; $conf['activesync']['version'] = '16.1'; // global EAS ceiling $conf['activesync']['storage'] = 'Sql'; $conf['activesync']['emailsync'] = true; $conf['activesync']['auth']['type'] = 'basic';
History ($conf['history']['enabled']) must be enabled -- ActiveSync relies on it for change tracking.
Clients expect two URL paths to reach Horde's RPC endpoint:
/Microsoft-Server-ActiveSync -- sync traffic/autodiscover/autodiscover.xml and /autodiscover/autodiscover.json -- Autodiscover (see below)In a Composer-based Horde 6 deployment, the web-readable entry point is web/horde/rpc.php (symlinked from the package tree). Your web server document root should point at the deployment's web/ directory.
Recommended rewrite rules (case-insensitive Autodiscover, v1 and v2):
<IfModule mod_rewrite.c>
RewriteEngine On
# Pass MS headers through mod_proxy_fcgi to PHP-FPM when needed:
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteRule .* - [E=HTTP_MS_ASPROTOCOLVERSION:%{HTTP:Ms-Asprotocolversion}]
RewriteRule .* - [E=HTTP_X_MS_POLICYKEY:%{HTTP:X-Ms-Policykey}]
# Autodiscover v1 (POX/XML) -- [NC] is required; iOS sends /Autodiscover/Autodiscover.xml
RewriteRule "^/autodiscover/autodiscover\.xml$" "/horde/rpc.php" [NC,L]
# Autodiscover v2 (JSON) -- no $ anchor; path continues with /v1.0/<email>
RewriteRule "^/autodiscover/autodiscover\.json" "/horde/rpc.php" [NC,L,QSA]
# ActiveSync
RewriteRule "^/Microsoft-Server-ActiveSync" "/horde/rpc.php" [L,QSA]
</IfModule>
Note: An Alias to rpc.php also works when PHP runs as mod_php in the same vhost. With mod_proxy_fcgi, prefer RewriteRule as shown. If Authorization headers are not passed to PHP, the E=HTTP_AUTHORIZATION rule above usually fixes it.
Note: Horde 6 uses symlinks under web/ into vendor/. Make sure rewrites target /horde/rpc.php relative to the vhost document root that serves Horde.
location /Microsoft-Server-ActiveSync {
rewrite ^ /horde/rpc.php last;
}
location ~* ^/autodiscover/autodiscover\.xml$ {
rewrite ^ /horde/rpc.php last;
}
location ~* ^/autodiscover/autodiscover\.json {
rewrite ^ /horde/rpc.php last;
}
Pass Authorization, Ms-Asprotocolversion, and X-Ms-Policykey headers to PHP-FPM. Increase fastcgi_read_timeout (or equivalent) for long-poll Ping requests -- see Reverse proxy below.
alias.url = (
"/Microsoft-Server-ActiveSync" => "/var/www/horde/web/horde/rpc.php",
"/autodiscover/autodiscover.xml" => "/var/www/horde/web/horde/rpc.php",
"/autodiscover/autodiscover.json" => "/var/www/horde/web/horde/rpc.php"
)
Use case-insensitive matching or multiple alias entries if your clients vary path casing.
Autodiscover tells the device where the ActiveSync server lives. Horde supports both Autodiscover protocols:
| Protocol | Request | Auth | Response |
|---|---|---|---|
| v2 JSON | GET /autodiscover/autodiscover.json/v1.0/<email>?Protocol=ActiveSync |
None | {"Protocol":"ActiveSync","Url":"https://.../Microsoft-Server-ActiveSync"} |
| v1 POX/XML | POST /autodiscover/autodiscover.xml |
HTTP Basic | XML with ActiveSync and optional IMAP/SMTP settings |
Modern clients (recent iOS, Outlook) try v2 first, then fall back to v1. Both must reach rpc.php via the web server rules above. A v2 404 alone is harmless (clients fall back), but routing v2 is recommended for faster account setup.
SSL is required for production Autodiscover. Self-signed certificates require the device to trust the CA.
Autodiscover is driven by the email domain -- the part after @. For user@example.com, the device queries example.com and autodiscover.example.com. It does not know that Horde might live on mail.example.com or webmail.example.com unless discovery succeeds and returns that URL.
This matters when Horde runs on a different host than the email domain (very common).
For user@example.com, a typical iOS/Outlook client tries, in order:
GET https://example.com/autodiscover/autodiscover.json/v1.0/user@example.com?Protocol=ActiveSync (v2)POST https://example.com/autodiscover/autodiscover.xml (v1)POST https://autodiscover.example.com/autodiscover/autodiscover.xml (v1)GET http://autodiscover.example.com/autodiscover/autodiscover.xml -- expects a 302 redirect to HTTPS, then POST_autodiscover._tcp.example.com -> POST to the target hostDNS is used in two ways: A/AAAA records for each hostname in steps 1-4, and an optional SRV record in step 5:
_autodiscover._tcp.example.com. 3600 IN SRV 0 1 443 mail.example.com.
Point the SRV target at the host that actually serves Horde Autodiscover.
Microsoft documentation uses mixed case (AutoDiscover/AutoDiscover.xml). Real clients often send /Autodiscover/Autodiscover.xml or /autodiscover/autodiscover.xml -- the difference is the d in the middle of the word. Apache RewriteRule is case-sensitive unless [NC] is set. Use [NC] on all Autodiscover rewrite rules.
When Horde and the email domain are the same host (e.g. https://example.com/horde for user@example.com), the Apache/nginx rules in Web server above are sufficient. No redirect is needed.
When email addresses use @example.com but Horde runs on https://mail.example.com, configure each email-domain vhost to forward Autodiscover to the Horde host.
On the email-domain vhost (e.g. example.com, autodiscover.example.com) -- not the Horde host:
RedirectMatch 302 (?i)/autodiscover/autodiscover.xml https://mail.example.com/autodiscover/autodiscover.xml
RedirectMatch 302 (?i)^(/autodiscover/autodiscover\.json.*)$ https://mail.example.com$1
Autodiscover clients follow the redirect and re-send the request. If a client drops the POST body on 302, use Redirect 307 instead.
Port 80 redirect for step 4 above:
<VirtualHost *:80>
ServerName autodiscover.example.com
RedirectMatch 302 (?i)/autodiscover/autodiscover.xml https://mail.example.com/autodiscover/autodiscover.xml
</VirtualHost>
Important: Do not install ActiveSync rewrite rules on vhosts that are not the Horde document root. If a non-Horde vhost rewrites Autodiscover to /horde/rpc.php but has no horde/ directory, requests return 404 and any RedirectMatch on the same vhost will not run (mod_rewrite is evaluated before mod_alias). Email-domain vhosts should only redirect to the Horde host; the Horde host vhost carries the rpc.php rewrites.
Horde must map the email address from the Autodiscover request to a Horde username. Configure this on the ActiveSync tab (autodiscovery settings). Hooks are available for custom logic -- see horde/config/hooks.php.dist (activesync_get_autodiscover_username, activesync_autodiscover_xml, activesync_autodiscover_parameters).
If Autodiscover authentication fails, the device falls back to manual setup.
| Test | Expected result |
|---|---|
POST https://mail.example.com/autodiscover/autodiscover.xml (no credentials) |
401 -- Horde asks for auth; routing works |
| Same request | 404 -- routing broken (check rewrite rules and vhost) |
GET .../autodiscover.json/v1.0/user@example.com?Protocol=ActiveSync |
**200** JSON with ActiveSync URL (if v2 routed) |
| Valid credentials on v1 POST | **200** XML with server URLs |
Example:
curl -ks -o /dev/null -w "%{http_code}\n" \
-X POST -H "Content-Type: text/xml" --data "<x/>" \
https://mail.example.com/autodiscover/autodiscover.xml
A 401 response with realm "Horde ActiveSync" means the request reached rpc.php correctly.
ActiveSync Ping holds connections open for up to 3540 seconds (depending on client and $conf['activesync']['ping']['heartbeatmax']). Reverse proxies must not time out sooner.
Apache backend example:
ProxyPass / https://192.168.1.230/ connectiontimeout=600 timeout=4000
ProxyPassReverse / https://192.168.1.230/
ProxyTimeout 5400
Adjust timeouts for your environment. Similar proxy_read_timeout / fastcgi_read_timeout settings apply for nginx.
Set max_execution_time to 0 or at least twice the maximum heartbeat interval. Configure this in Horde's general configuration tab or in php.ini / the PHP-FPM pool.
Administrators manage ActiveSync under Horde administration -> ActiveSync and ActiveSync Devices.
Key options on the ActiveSync tab:
| Setting | Purpose |
|---|---|
| Enable ActiveSync | Master switch |
| Storage backend | Sql or Nosql (MongoDB) for device state |
| Highest EAS version | Global protocol ceiling (2.5 ... 16.1) |
| Email sync | Enable/disable mail collections |
| Autodiscovery | Username mapping from email address |
| Logging | Path, level, per-device log files |
| Ping heartbeat min/max | Long-poll interval bounds |
Per-user EAS version limits are set under Permissions -> ActiveSync -> Maximum ActiveSync protocol version -- not in user preferences. Per-device limits use the activesync_device_version hook with version_mode = device in conf.php.
Provisioning mode: None, Allow, or Force -- see Provisioning/Remote Wipe below.
Device management: administrators can view all paired devices, request remote wipe, force re-provisioning, and block devices. Per-device protocol log files are supported (logging.type = perdevice); a per-device log toggle in the admin GUI is still on the roadmap.
Microsoft documentation on EAS security policies.
No additional steps are normally required for synchronization of the supported applications. Each application has user preferences for which shares are synchronized. For example, in Kronolith the user's default calendar is always synchronized; additional owned calendars can be selected.
Starting with Horde 5.2, applications can expose discrete calendars, address books, task lists, and note pads on the client instead of multiplexing them into a single collection. Not all EAS clients support this.
activesync_device_modify hook (horde/config/hooks.php.dist) for device-specific overrides.All sync-able sources must be writable by the user.
Users manage paired devices under Global Preferences -> ActiveSync Devices (force re-sync, remote wipe).
Email synchronization can be disabled on the ActiveSync tab. ActiveSync email requires IMAP; POP3 is not supported. In the Horde stack, IMP's configured IMAP server is used.
QRESYNC and per-mailbox MODSEQ on the IMAP server greatly improve performance. An IMAP proxy can also help.
Supported mail flags: seen and flagged for follow up. Flag-only changes trigger a sync when MODSEQ is available; otherwise a new message arrival triggers sync and flag changes are picked up in the same pass.
ActiveSync does not support the IMAP \Deleted flag. Messages flagged deleted in a desktop MUA are not removed on the device until expunged. Configure a Trash folder in IMP for device-side deletes to move messages instead of immediate expunge.
S/MIME signing and encryption work when IMP S/MIME is configured. Native iOS and Outlook clients generally support S/MIME over ActiveSync. Most Android native mail clients do not support S/MIME; third-party clients may.
Horde 6 supports EAS 2.5 through 16.1. See Supported ActiveSync Features for the full matrix.
Notable version milestones:
| EAS version | Exchange equivalent | Highlights |
|---|---|---|
| 12.0 / 12.1 | 2007 | HTML mail, follow-up flags, WBXML provisioning |
| 14.0 / 14.1 | 2010 | Outlook 2013+ sync, extended search |
| 16.0 | 2016 | Calendar instance model, draft sync, mailbox Find/KQL |
| 16.1 | 2016+ | Propose-new-time (COUNTER), account-only remote wipe |
Version negotiation: the server advertises a ceiling (conf['activesync']['version']); the client sends MS-ASProtocolVersion per request. Per-user permissions and per-device hooks can lower (or in some cases raise) the ceiling for individual accounts or devices.
Provisioning registers devices with the server and enables policy enforcement (PIN, complexity, failed-attempt limits) and remote wipe.
Enable provisioning via the permissions tree: Horde -> ActiveSync -> Provisioning -> individual policies.
| Mode | Behavior |
|---|---|
| None | No provisioning; remote wipe disabled |
| Allow | Provision devices that support it; others may still connect |
| Force | Only provisioned devices may connect |
EAS 16.1 adds account-only remote wipe for devices negotiating >= 16.1.
Users and administrators initiate wipe from the device management UI. The wipe command is sent on the device's next non-PING request. Status Pending can be cancelled; status Wiped requires removing the device entry before it can pair again.
Contacts, calendar, tasks, notes, and email all sync through Horde ActiveSync. Current iOS Mail, Android (Gmail, Samsung), Outlook 2013+, and Windows Mail generally work with EAS 14.1+. EAS 16.x features require clients and server configuration that negotiate 16.0 or 16.1.
Not all devices support tasks or notes natively. Check known issues and broken client behavior before troubleshooting a specific client.
For the complete feature set by protocol version, see Supported ActiveSync Features.
Create a new account of type Microsoft Exchange, Exchange, or Corporate. Enter the Horde username and password.
With Autodiscover configured, enter only the email address and password -- the device discovers the server. Without Autodiscover, set the server to the hostname of the vhost that serves Horde (e.g. mail.example.com), not the full URL path. The ActiveSync path /Microsoft-Server-ActiveSync is implied.
SSL should be enabled for production. Self-signed certificates must be trusted on the device.
After setup, select which folders/collections to synchronize. Some clients require a manual folder refresh after the initial sync.
Outlook 2013 and later can sync via ActiveSync (requires EAS >= 14.0). ActiveSync does not provide full Exchange functionality in Outlook.
With Autodiscover working, use the standard email account wizard. For manual setup, choose Outlook.com or Exchange ActiveSync compatible service -- not Microsoft Exchange Server or compatible service.
Outlook does not use EAS for Free/Busy lookup. Provide Kronolith's Free/Busy URL under File -> Options -> Calendar -> Free/Busy Options:
https://example.com/horde/kronolith/fb.php?u=%NAME%
%NAME% is replaced with the mailbox user portion of the meeting address.
First, check known issues and the bug tracker.
Autodiscover fails / account setup loops:
POST .../autodiscover.xml -> 401 without credentials)./horde/rpc.php.[NC] on Autodiscover rewrite rules (path case).example.com / autodiscover.example.com, optional SRV record.Setup succeeds but no data appears:
Collecting diagnostics:
logging.type = perdevice.For unencrypted HTTP debugging:
tshark 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' -w /path/to/capture.pcap
SSL capture requires the server's RSA private key and non-forward-secret ciphers -- use only on test systems.
Historical milestones and current Horde 6 work. For protocol-level detail on EAS 16, see the horde/activesync README. Open library refactor items are tracked in the package doc/todo.md.
| Feature | Status |
|---|---|
| EAS 14(.1) support. | Complete. |
| Improved device management via hooks. | Complete. |
| Support for SOFTDELETE. | Complete. |
| Feature | Status |
|---|---|
| Improved email identity support. | Complete. |
| Support for multiple sources per collection. E.g., multiple calendars, addressbooks etc... | Complete. |
| Improved device management GUI. | Complete. |
| Feature | Status |
|---|---|
| EAS 16.0 support in applications. | Complete (calendar instance model, draft sync, Find/KQL, ClientUid, Location, SmartForward Forwardee). |
| EAS 16.1 support. | Complete (propose-new-time / METHOD=COUNTER, DisallowNewTimeProposal, account-only remote wipe). |
| Per-user EAS version ceiling via permissions. | Complete (horde:activesync:version). |
| Per-device EAS version ceiling via hook. | Complete (activesync_device_version with version_mode = device). |
| EAS 16.0 Find command (mailbox/GAL search). | Complete -- KQL parser in horde/activesync, IMP search backend in horde/core. |
Autodiscover v2 JSON (autodiscover.json). |
Complete in PHP; requires web server routing (see Autodiscover above). |
| Autodiscover v2 JSON response hardening. | Complete (Protocol default, json_encode). |
| Multiplexed PIM folders (multiple calendars, address books, task lists, note pads). | Complete. |
| Feature | Status |
|---|---|
| EAS 16.0 recurring calendar instance sync. | Complete -- bound/modified instances, exception deletion (horde/kronolith). |
Meeting invitations in mail (recurring RRULE / RECURRENCE-ID in MeetingRequest). |
Complete. |
MeetingResponse RSVP and propose-new-time from device. |
Complete -- includes InstanceId -> RECURRENCE-ID mapping. |
| IMP iTip RSVP UI above invitation mail body. | Complete. |
| EAS 16.0 draft mail sync (create/edit/send drafts on device). | Complete. |
FILTERTYPE_INCOMPLETETASKS for task collections. |
Complete (horde/core / horde/nag). |
| Feature | Status |
|---|---|
| SQL/Mongo row locks for parallel device state access. | Complete. |
Reject and repair corrupt collection sync_data. |
Complete. |
| Separate PING watermark from SYNC modseq (iOS mail loop fix). | Complete. |
FOLDERSYNC_REQUIRED loop guard for broken clients. |
Complete. |
| Modern iOS Notes multiplexing behaviour. | Complete. |
| PING orphan-collection and hierarchy-sync recovery. | Complete. |
ItemOperations fetch fixes (deleted messages, folder type). |
Complete. |
| Feature | Status |
|---|---|
| Improved device management GUI (grouped by user, collection details, account wipe). | Complete. |
Per-device protocol log files (logging.type = perdevice). |
Complete. |
| Ability for admin to toggle sync log on/off per device from GUI and view via GUI. | Planned. |
| CLI admin tool. | If sponsored. |
| EAS usage dashboard / live operator monitor. | Planned (separate admin tool; not in protocol library). |
| Feature | Status |
|---|---|
| SMS synchronization. | Planned -- today SMS collections are stubbed so broken clients do not break mail sync; full backend support targeted for Horde 6 refactor. |
ItemOperations:Schema requests. |
Deferred -- no known client requires this. |
HTTP 503 throttling (X-MS-Throttle). |
Deferred -- relevant only at high load. |
Task Regenerate=1 (Outlook post-completion regeneration). |
Not supported -- Nag uses completions[], not regenerated due dates. |
Horde 6 library refactor (PSR-4 class names, horde/http request/response, pluggable driver subsystems, storage layer rename). |
Planned -- breaking changes; see horde/activesync doc/todo.md. |
EAS 16.0/16.1 application support shipped in the Horde 6 / ActiveSync 3.x line on FRAMEWORK_6_0. See Supported ActiveSync Features for the version matrix.
Horde_ActiveSync protocol handling was originally based on Z-Push but has been heavily refactored. Key advantages of Horde's stack:
Horde_ActiveSync_Driver_Base backends; state and diff logic are separate.Horde_Imap_Client with QRESYNC instead of the PHP c-client extension.