Emergency server help: get in touch

PJSIP Endpoint Unreachable or Unavailable: Fix Qualify and NAT

Fix PJSIP endpoints and contacts showing Unreachable or Unavailable: what qualify does, how to read contact status, and fixing NAT keepalives, firewalls and stale contacts.

Published 7 min read

Short answer: Asterisk marks a PJSIP contact Unreachable (shown as Unavail) when its qualify OPTIONS request gets no answer within qualify_timeout (3 seconds by default) or hits a transport error; when all contacts of an endpoint are unreachable, the endpoint shows Unavailable. The usual causes are an expired NAT pinhole, a firewall dropping the OPTIONS or its reply, SIP ALG, or a stale contact. Check with pjsip show contacts, watch OPTIONS with pjsip set logger method OPTIONS, and fix NAT keepalives or qualify_frequency.

We ran these commands on our lab server (Debian 12, FreePBX 17.0.33, Asterisk 22.11) on 6 October 2026 and checked status names and qualify behaviour against the Asterisk 22 source and documentation (linked below). The lab has no registered phones, so contact output below is described rather than pasted.

What qualify does and what the statuses mean

Qualify is a health check. Every qualify_frequency seconds, Asterisk sends a SIP OPTIONS request to each contact of an AOR and waits up to qualify_timeout seconds for any reply. The result sets the contact status. These are the labels used in the Asterisk 22 source (pjsip_options.c):

Status (AMI / events)Short form in pjsip show contactsMeaning
ReachableAvailThe contact answered the last OPTIONS request
UnreachableUnavailNo answer before the timeout, or a transport error
NonQualifiedNonQualQualify is off for this AOR (qualify_frequency=0)
UnknownUnknownNot tested yet, for example just after a restart

Two details from the source are worth knowing:

  • Any SIP response counts as reachable, even 401 or 404, unless you set qualify_2xx_only=yes on the AOR (added in Asterisk 20.12.0, 21.7.0 and 22.2.0). Only a timeout or a transport failure makes a contact Unreachable.
  • Endpoint state follows its contacts. When the last reachable contact goes away, Asterisk logs Endpoint 201 is now Unreachable and pjsip show endpoints shows the endpoint as Unavailable. Calls to it fail at once instead of ringing.

Contact changes are logged at verbose level 3 as Contact 201/sip:... is now Unreachable. RTT: ..., which makes /var/log/asterisk/full the first place to look for when a phone dropped.

Check the current state

asterisk -rx "pjsip show endpoints"
asterisk -rx "pjsip show contacts"
asterisk -rx "pjsip show aor 201"
asterisk -rx "pjsip show qualify endpoint 201"
  • pjsip show contacts lists each contact URI, its status and the round-trip time (RTT) of the last qualify. Filter with pjsip show contacts like ^201.
  • pjsip show aor 201 shows qualify_frequency, qualify_timeout, max_contacts and the contacts bound to it.
  • pjsip show qualify endpoint 201 prints the current qualify options for all AORs on that endpoint.

Force a fresh test instead of waiting for the next interval:

asterisk -rx "pjsip qualify 201"

Find when it dropped:

grep -E "Contact 201/|Endpoint 201 is now" /var/log/asterisk/full | tail -20

A contact that flaps between Reachable and Unreachable every few minutes almost always points to NAT timing. A contact that is Unreachable all the time points to a firewall, a wrong contact address or a device that is really offline.

Watch the OPTIONS traffic

The PJSIP logger can be limited to one method and one host, so you see only qualify traffic for the phone in question (syntax from Asterisk 22’s res_pjsip_logger):

asterisk -rvvv
pjsip set logger host 198.51.100.20
pjsip set logger method OPTIONS
pjsip qualify 201
pjsip set logger off

What to look for:

  • OPTIONS sent, nothing comes back: the packet is not reaching the phone (NAT pinhole closed, firewall) or the reply is dropped on the way back.
  • OPTIONS sent to a private address such as 192.168.x.x from a server on the internet: the phone registered its LAN IP and Asterisk is not rewriting the contact. Set rewrite_contact=yes.
  • Reply arrives from a different port than the request went to: symmetric NAT or SIP ALG rewriting. Use force_rport=yes and disable SIP ALG.
  • Reply is 401, 403 or 404: the contact still counts as reachable unless qualify_2xx_only is set.

For long captures, pjsip set logger pcap /tmp/options.pcap writes a file you can open in Wireshark or sngrep. Our sngrep guide shows how to follow a single dialog.

Fix NAT keepalives and pinholes

A phone behind NAT registers from a public IP and port that the router keeps open only for a while after the last packet. UDP timeouts vary by router, and some are short. If nothing passes in that window, the next OPTIONS from Asterisk is dropped and the contact goes Unreachable until the phone re-registers.

Keep the mapping open from one side or both:

  • Shorter qualify interval: set qualify_frequency on the AOR below the router’s UDP timeout, for example 25 or 30 seconds if you do not know it. Each OPTIONS and its reply refresh the mapping. In FreePBX it is the Qualify Frequency field (default 60 for extensions and trunks).
  • Phone keepalives: enable NAT keep-alive on the phone (vendors name it differently) at 15-30 seconds.
  • Shorter registration expiry on the phone, so it re-registers before the mapping closes.
  • TCP or TLS transport: connection-oriented transports keep one connection open. For these, the global keep_alive_interval (default 90 seconds) makes Asterisk send keepalives.

And make sure Asterisk talks back to the right address:

[201]
type=endpoint
rewrite_contact=yes
force_rport=yes
rtp_symmetric=yes
direct_media=no

[201]
type=aor
max_contacts=1
remove_existing=yes
qualify_frequency=30

On FreePBX these are fields on the extension’s Advanced tab; do not edit the generated pjsip.*.conf files. Our PJSIP NAT generator outputs the full set for your layout and PJSIP behind NAT explains each option.

Other causes: firewalls, ALG, stale contacts

  • Server firewall or fail2ban. If the phone’s public IP was banned after a burst of failed registrations, OPTIONS replies are dropped. Check fail2ban-client status asterisk-iptables and unban if needed.
  • SIP ALG on the phone’s router rewrites headers and can break the reply path. See Disable SIP ALG.
  • Stale contacts. With max_contacts=1 and remove_existing=no, a phone that changes IP can be refused or leave an old contact that will never answer. remove_existing=yes replaces the old one; remove_unavailable=yes (Asterisk 16.22, 18.8 and later) prefers removing unreachable contacts.
  • Qualify timeout too short for a slow link: raise qualify_timeout (fractional seconds, default 3.0) for satellite or high-latency mobile links.
  • Trunks marked Unavailable: some providers do not answer OPTIONS from customers. If calls work but the trunk always shows Unavail, set its Qualify Frequency to 0 and rely on registration status instead.

Check that it worked

  1. pjsip show contacts shows the contact as Avail with a stable RTT.
  2. Leave it for longer than the old failure interval (for example 15 minutes) and confirm no new is now Unreachable lines in the log.
  3. Call the extension from another phone after a long idle period; it should ring every time.
  4. If you changed transports, remember a full restart is needed for transport changes.

Official documentation: Asterisk: res_pjsip configuration options · Asterisk: ContactStatusDetail AMI event · Asterisk source: pjsip_options.c

Related: PJSIP Behind NAT: Asterisk and FreePBX Settings for Two-Way Audio · PJSIP NAT Settings Generator: Asterisk, FreePBX, Issabel, VitalPBX, 3CX · Troubleshoot SIP Calls with sngrep and the Asterisk PJSIP Logger · Disable SIP ALG: 7 Router Fixes for One-Way Audio and Dropped Calls · VoIP One-Way Audio Fix: 6 Checks for NAT and RTP

See also: chan_sip to PJSIP Migration: sip_to_pjsip.py, Option Mapping, Tests · sip.conf to pjsip.conf Converter for Asterisk · Asterisk CLI Commands Cheat Sheet: PJSIP, Calls, Dialplan, Logs · FreePBX PJSIP Trunk Setup: Registration, IP Auth, Routes, NAT

Frequently asked questions

What does Unavail mean in pjsip show contacts?

The contact did not answer the last qualify OPTIONS request within qualify_timeout, or there was a transport error. Asterisk treats it as Unreachable until a later qualify succeeds.

What is a good qualify_frequency value?

For phones behind NAT, 25 to 30 seconds is a common starting point; go lower if contacts still drop. For phones on the LAN or TCP/TLS, 60 seconds is fine. 0 disables qualify.

Why is my endpoint Unavailable when the phone looks registered?

Registration and qualify are separate. The phone can register outward while Asterisk’s OPTIONS requests inward are dropped by NAT or a firewall.

Does a 401 reply to OPTIONS count as reachable?

Yes. Any SIP response marks the contact Reachable unless qualify_2xx_only=yes is set on the AOR.

Can I stop qualify on a trunk that never answers OPTIONS?

Yes. Set qualify_frequency to 0 (Qualify Frequency in FreePBX) and monitor the registration status instead.

Free website test

Is your website set up right?

Check SSL, security headers, redirects, robots.txt, sitemap, llms.txt and security.txt in one test. It takes about 30 seconds.