diff --git a/src/osdep/osdep.c b/src/osdep/osdep.c
index 23d04cf8e..5863d6961 100644
--- a/src/osdep/osdep.c
+++ b/src/osdep/osdep.c
@@ -1018,3 +1018,84 @@ get_system_str(int xwin)
 {
 	return xwin ? SYSTEM_STR "-xwin" : SYSTEM_STR;
 }
+
+#if _DEFAULT_SOURCE || _SVID_SOURCE || _BSD_SOURCE
+
+/* tempnam() replacement without races */
+
+int isdirectory(char *path) {
+	struct stat ss;
+	if (path == NULL)
+		return 0;
+	if (-1 == stat(path, &ss))
+		return 0;
+	return S_ISDIR(ss.st_mode);
+}
+
+char *tempname(char *dir, char *pfx, char *suff) {
+	struct string path;
+	char *ret;
+	int fd;
+
+	if (isdirectory(getenv("TMPDIR")))
+		dir = getenv("TMPDIR");
+	else if (dir != NULL)
+		dir = dir;
+	else if (isdirectory(P_tmpdir))
+		dir = P_tmpdir;
+	else if (isdirectory("/tmp"))
+		dir = "/tmp";
+	else {
+		errno = ENOTDIR;
+		return NULL;
+	}
+
+	if (!init_string(&path)) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	add_to_string(&path, dir);
+	add_to_string(&path, "/");
+	add_to_string(&path, pfx);
+	add_to_string(&path, "XXXXXX");
+	if (suff)
+		add_shell_safe_to_string(&path, suff, strlen(suff));
+
+	fd = mkstemps(path.source, suff ? strlen(suff) : 0);
+	if (fd == -1) {
+		done_string(&path);
+		errno = ENOENT;
+		return NULL;
+	}
+	close(fd);
+
+	ret = strdup(path.source);
+	done_string(&path);
+	return ret;
+}
+
+#else
+
+#warning mkstemps does not exist, using tempnam
+char *tempname(char *dir, char *pfx, char *suff) {
+	char *temp, *ret;
+	struct string name;
+
+	temp = tempnam(dir, pfx);
+	if (temp == NULL)
+		return NULL;
+
+	if (!init_string(&name)) {
+		free(temp);
+		return NULL;
+	}
+	add_to_string(&name, temp);
+	free(temp);
+	add_shell_safe_to_string(&name, suff, strlen(suff));
+
+	ret = strdup(name.source);
+	done_string(&name);
+	return ret;
+}
+
+#endif
diff --git a/src/osdep/osdep.h b/src/osdep/osdep.h
index 046aeb511..06a9eac98 100644
--- a/src/osdep/osdep.h
+++ b/src/osdep/osdep.h
@@ -52,6 +52,7 @@ int resize_window(int, int, int, int);
 int can_resize_window(int);
 int can_open_os_shell(int);
 void set_highpri(void);
+char *tempname(char *dir, char *pfx, char *suff);
 
 #ifdef USE_OPEN_PREALLOC
 int open_prealloc(char *, int, int, off_t);
diff --git a/src/session/download.c b/src/session/download.c
index a0f1c4fdd..d4efa348d 100644
--- a/src/session/download.c
+++ b/src/session/download.c
@@ -800,7 +800,7 @@ lookup_unique_name(struct terminal *term, char *ofile,
 	}
 
 	/* Check if the file already exists (file != ofile). */
-	file = get_unique_name(ofile);
+	file = (flags & DOWNLOAD_OVERWRITE) ? ofile : get_unique_name(ofile);
 
 	if (!file || overwrite == 1 || file == ofile) {
 		/* Still nothing special to do... */
@@ -885,7 +885,9 @@ create_download_file_do(struct terminal *term, char *file,
 	 * --pasky */
 	h = open(file, O_CREAT | O_WRONLY
 			| (flags & DOWNLOAD_RESUME_SELECTED ? 0 : O_TRUNC)
-			| (sf && !(flags & DOWNLOAD_RESUME_SELECTED) ? O_EXCL : 0),
+			| (sf &&
+			   !(flags & DOWNLOAD_RESUME_SELECTED) &&
+			   !(flags & DOWNLOAD_OVERWRITE) ? O_EXCL : 0),
 		 sf ? 0600 : 0666);
 	saved_errno = errno; /* Saved in case of ... --Zas */
 
@@ -999,31 +1001,17 @@ create_download_file(struct terminal *term, char *fi,
 static char *
 get_temp_name(struct uri *uri)
 {
-	struct string name;
 	char *extension;
-	/* FIXME
-	 * We use tempnam() here, which is unsafe (race condition), for now.
-	 * This should be changed at some time, but it needs an in-depth work
-	 * of whole download code. --Zas */
-	char *nm = tempnam(NULL, ELINKS_TEMPNAME_PREFIX);
-
-	if (!nm) return NULL;
-
-	if (!init_string(&name)) {
-		free(nm);
-		return NULL;
-	}
-
-	add_to_string(&name, nm);
-	free(nm);
+	char *nm;
 
 	extension = get_extension_from_uri(uri);
-	if (extension) {
-		add_shell_safe_to_string(&name, extension, strlen(extension));
-		mem_free(extension);
-	}
+	if (!extension)
+		extension = stracpy("");
 
-	return name.source;
+	nm = tempname(NULL, ELINKS_TEMPNAME_PREFIX, extension);
+	mem_free(extension);
+
+	return nm;
 }
 
 
@@ -1321,7 +1309,6 @@ continue_download(void *data, char *file)
 	}
 
 	if (type_query->external_handler) {
-		/* FIXME: get_temp_name() calls tempnam(). --Zas */
 		file = get_temp_name(type_query->uri);
 		if (!file) {
 			mem_free(codw_hop);
@@ -1338,7 +1325,9 @@ continue_download(void *data, char *file)
 	create_download_file(type_query->ses->tab->term, file,
 			     &codw_hop->real_file,
 			     type_query->external_handler
-			     ? DOWNLOAD_RESUME_ALLOWED | DOWNLOAD_EXTERNAL
+			     ? DOWNLOAD_RESUME_ALLOWED |
+			       DOWNLOAD_EXTERNAL |
+			       DOWNLOAD_OVERWRITE
 			     : DOWNLOAD_RESUME_ALLOWED,
 			     continue_download_do, codw_hop);
 }
diff --git a/src/session/download.h b/src/session/download.h
index 3d3085f7c..0fd657342 100644
--- a/src/session/download.h
+++ b/src/session/download.h
@@ -39,7 +39,12 @@ enum download_flags {
 	DOWNLOAD_RESUME_SELECTED = 2,
 
 	/** The file will be opened in an external handler.  */
-	DOWNLOAD_EXTERNAL = 4
+	DOWNLOAD_EXTERNAL = 4,
+
+	/** File overwriting is allowed. This is for temp names, since
+	 * the file is created by the same function that chooses its
+	 * name.  */
+	DOWNLOAD_OVERWRITE = 8
 };
 
 struct download {