diff --git a/opal-core-ws/src/main/java/org/obiba/opal/web/security/CSRFInterceptor.java b/opal-core-ws/src/main/java/org/obiba/opal/web/security/CSRFInterceptor.java
index 9cdb8c1b97..50c04ba153 100644
--- a/opal-core-ws/src/main/java/org/obiba/opal/web/security/CSRFInterceptor.java
+++ b/opal-core-ws/src/main/java/org/obiba/opal/web/security/CSRFInterceptor.java
@@ -40,28 +40,36 @@ public class CSRFInterceptor extends AbstractSecurityComponent implements Reques
 
   private static final String REFERER_HEADER = "Referer";
 
+  private static final String USER_AGENT_HEADER = "User-Agent";
+
   private static final Pattern localhostPattern = Pattern.compile("^http[s]?://localhost:.*");
 
   private static final Pattern loopbackhostPattern = Pattern.compile("^http[s]?://127\\.0\\.0\\.1:.*");
 
   private final boolean productionMode;
 
-  private final List<String> csrfAllowed;
+  private final List<String> csrfAllowedHosts;
+
+  private final List<String> csrfAllowedAgents;
 
   @Autowired
   public CSRFInterceptor(@Value("${productionMode}") boolean productionMode,
-                         @Value("${csrf.allowed}") String csrfAllowed) {
+                         @Value("${csrf.allowed}") String csrfAllowedHosts,
+                         @Value("${csrf.allowed-agents}") String csrfAllowedAgents) {
     this.productionMode = productionMode;
-    this.csrfAllowed = Strings.isNullOrEmpty(csrfAllowed) ? Lists.newArrayList() : Splitter.on(",").splitToList(csrfAllowed.trim());
+    this.csrfAllowedHosts = Strings.isNullOrEmpty(csrfAllowedHosts) ? Lists.newArrayList() : Splitter.on(",").splitToList(csrfAllowedHosts.trim());
+    this.csrfAllowedAgents = Strings.isNullOrEmpty(csrfAllowedAgents) ? Lists.newArrayList() : Splitter.on(",").splitToList(csrfAllowedAgents.trim());
   }
 
   @Override
   public void preProcess(HttpServletRequest httpServletRequest, ResourceMethodInvoker resourceMethod, ContainerRequestContext requestContext) {
-    if (!productionMode || csrfAllowed.contains("*")) return;
+    if (!productionMode || csrfAllowedHosts.contains("*")) return;
 
     String host = requestContext.getHeaderString(HOST_HEADER);
+    if (matchesLocalhost(host)) return;
+
     String referer = requestContext.getHeaderString(REFERER_HEADER);
-    if (referer != null) {
+    if (!Strings.isNullOrEmpty(referer)) {
       String refererHostPort = "";
       try {
         URI refererURI = URI.create(referer);
@@ -70,20 +78,22 @@ public void preProcess(HttpServletRequest httpServletRequest, ResourceMethodInvo
         // malformed url
       }
       // explicitly ok
-      if (csrfAllowed.contains(refererHostPort)) return;
-
-      boolean forbidden = false;
-      if (!matchesLocalhost(host) && !referer.startsWith(String.format("https://%s/", host))) {
-        forbidden = true;
-      }
+      if (csrfAllowedHosts.contains(refererHostPort)) return;
 
+      boolean forbidden = !referer.startsWith(String.format("https://%s/", host));
       if (forbidden) {
         log.warn("CSRF detection: Host={}, Referer={}", host, referer);
         log.info(">> You can add {} to csrf.allowed setting", refererHostPort);
         throw new ForbiddenException("CSRF error");
       }
+    } else {
+      String userAgent = requestContext.getHeaderString(USER_AGENT_HEADER);
+      if (Strings.isNullOrEmpty(userAgent) || !matchesUserAgent(userAgent)) {
+        log.warn("CSRF detection: Host={}, User-Agent={}", host, userAgent);
+        log.info(">> Ensure 'Referer' HTTP header is set or allow this 'User-Agent' with 'csrf.allowed-agents' setting");
+        throw new ForbiddenException("CSRF error");
+      }
     }
-    return;
   }
 
   private boolean matchesLocalhost(String host) {
@@ -93,13 +103,8 @@ private boolean matchesLocalhost(String host) {
         || host.startsWith("127.0.0.1:");
   }
 
-  static String asHeader(Iterable<String> values) {
-    StringBuilder sb = new StringBuilder();
-    for (String s : values) {
-      if (!sb.isEmpty()) sb.append(", ");
-      sb.append(s);
-    }
-    return sb.toString();
+  private boolean matchesUserAgent(String userAgent) {
+    return csrfAllowedAgents.stream().anyMatch(ua -> userAgent.toLowerCase().contains(ua.toLowerCase()));
   }
 
 }
diff --git a/opal-core/src/main/resources/META-INF/defaults.properties b/opal-core/src/main/resources/META-INF/defaults.properties
index b3ae1b5cf3..61c3c609ff 100644
--- a/opal-core/src/main/resources/META-INF/defaults.properties
+++ b/opal-core/src/main/resources/META-INF/defaults.properties
@@ -73,8 +73,10 @@ apps.registration.exclude=
 apps.discovery.interval = 10000
 
 # CSRF
-# allowed referers, comma separated <host:port>
+# allowed referrers, comma separated <host:port>
 csrf.allowed=
+# allowed user agents when referrer is not specified
+csrf.allowed-agents=curl,python
 
 # CORS
 # use * as wildcard, separate origins with commas